[shared-struct] Prototype JS shared structs

Unlike the Stage 1 proposal, for simplicity the prototype does not add
any new syntax, instead opting for exposing a SharedStructType
constructor which takes an array of field names. This type constructor
returns constructors for shared structs.

Shared structs can be shared across Isolates, are fixed layout, have no
prototype, have no .constructor, and can only store primitives and
other shared structs.

The initial prototype does not have TurboFan support.

Bug: v8:12547
Change-Id: I23bdd819940b42139692bcdb53d372099b0d4426
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3390643
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79156}
This commit is contained in:
Shu-yu Guo 2022-02-16 12:10:57 -08:00 committed by V8 LUCI CQ
parent 3ecb92e3b7
commit 1025bf26e3
54 changed files with 874 additions and 101 deletions

View File

@ -855,6 +855,7 @@ filegroup(
"src/objects/js-regexp-string-iterator.tq",
"src/objects/js-regexp.tq",
"src/objects/js-shadow-realms.tq",
"src/objects/js-struct.tq",
"src/objects/js-temporal-objects.tq",
"src/objects/js-weak-refs.tq",
"src/objects/literal-objects.tq",
@ -1054,6 +1055,7 @@ filegroup(
"src/builtins/builtins-shadow-realms.cc",
"src/builtins/builtins-sharedarraybuffer.cc",
"src/builtins/builtins-string.cc",
"src/builtins/builtins-struct.cc",
"src/builtins/builtins-symbol.cc",
"src/builtins/builtins-temporal.cc",
"src/builtins/builtins-trace.cc",
@ -1644,6 +1646,8 @@ filegroup(
"src/objects/js-regexp.h",
"src/objects/js-shadow-realms.h",
"src/objects/js-shadow-realms-inl.h",
"src/objects/js-struct.h",
"src/objects/js-struct-inl.h",
"src/objects/js-temporal-objects.h",
"src/objects/js-temporal-objects-inl.h",
"src/objects/js-temporal-objects.cc",

View File

@ -1779,6 +1779,7 @@ torque_files = [
"src/objects/js-regexp-string-iterator.tq",
"src/objects/js-regexp.tq",
"src/objects/js-shadow-realms.tq",
"src/objects/js-struct.tq",
"src/objects/js-temporal-objects.tq",
"src/objects/js-weak-refs.tq",
"src/objects/literal-objects.tq",
@ -3190,6 +3191,8 @@ v8_header_set("v8_internal_headers") {
"src/objects/js-segments.h",
"src/objects/js-shadow-realms-inl.h",
"src/objects/js-shadow-realms.h",
"src/objects/js-struct-inl.h",
"src/objects/js-struct.h",
"src/objects/js-temporal-objects-inl.h",
"src/objects/js-temporal-objects.h",
"src/objects/js-weak-refs-inl.h",
@ -4064,6 +4067,7 @@ v8_source_set("v8_base_without_compiler") {
"src/builtins/builtins-shadow-realms.cc",
"src/builtins/builtins-sharedarraybuffer.cc",
"src/builtins/builtins-string.cc",
"src/builtins/builtins-struct.cc",
"src/builtins/builtins-symbol.cc",
"src/builtins/builtins-temporal.cc",
"src/builtins/builtins-trace.cc",

View File

@ -17,6 +17,7 @@
#include 'src/objects/js-promise.h'
#include 'src/objects/js-regexp-string-iterator.h'
#include 'src/objects/js-shadow-realms.h'
#include 'src/objects/js-struct.h'
#include 'src/objects/js-weak-refs.h'
#include 'src/objects/objects.h'
#include 'src/objects/source-text-module.h'

View File

@ -123,7 +123,8 @@ class BaseCollectionsAssembler : public CodeStubAssembler {
TNode<IntPtrT> EstimatedInitialSize(TNode<Object> initial_entries,
TNode<BoolT> is_fast_jsarray);
void GotoIfNotJSReceiver(const TNode<Object> obj, Label* if_not_receiver);
void GotoIfCannotBeWeakKey(const TNode<Object> obj,
Label* if_cannot_be_weak_key);
// Determines whether the collection's prototype has been modified.
TNode<BoolT> HasInitialCollectionPrototype(Variant variant,
@ -522,10 +523,14 @@ TNode<IntPtrT> BaseCollectionsAssembler::EstimatedInitialSize(
[=] { return IntPtrConstant(0); });
}
void BaseCollectionsAssembler::GotoIfNotJSReceiver(const TNode<Object> obj,
Label* if_not_receiver) {
GotoIf(TaggedIsSmi(obj), if_not_receiver);
GotoIfNot(IsJSReceiver(CAST(obj)), if_not_receiver);
void BaseCollectionsAssembler::GotoIfCannotBeWeakKey(
const TNode<Object> obj, Label* if_cannot_be_weak_key) {
GotoIf(TaggedIsSmi(obj), if_cannot_be_weak_key);
TNode<Uint16T> instance_type = LoadMapInstanceType(LoadMap(CAST(obj)));
GotoIfNot(IsJSReceiverInstanceType(instance_type), if_cannot_be_weak_key);
// TODO(v8:12547) Shared structs should only be able to point to shared values
// in weak collections. For now, disallow them as weak collection keys.
GotoIf(IsJSSharedStructInstanceType(instance_type), if_cannot_be_weak_key);
}
TNode<Map> BaseCollectionsAssembler::GetInitialCollectionPrototype(
@ -2723,17 +2728,18 @@ TF_BUILTIN(WeakMapLookupHashIndex, WeakCollectionsBuiltinsAssembler) {
auto table = Parameter<EphemeronHashTable>(Descriptor::kTable);
auto key = Parameter<Object>(Descriptor::kKey);
Label if_not_found(this);
Label if_cannot_be_weak_key(this);
GotoIfNotJSReceiver(key, &if_not_found);
GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);
TNode<IntPtrT> hash = LoadJSReceiverIdentityHash(CAST(key), &if_not_found);
TNode<IntPtrT> hash =
LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
TNode<IntPtrT> capacity = LoadTableCapacity(table);
TNode<IntPtrT> key_index =
FindKeyIndexForKey(table, key, hash, EntryMask(capacity), &if_not_found);
TNode<IntPtrT> key_index = FindKeyIndexForKey(
table, key, hash, EntryMask(capacity), &if_cannot_be_weak_key);
Return(SmiTag(ValueIndexFromKeyIndex(key_index)));
BIND(&if_not_found);
BIND(&if_cannot_be_weak_key);
Return(SmiConstant(-1));
}
@ -2788,22 +2794,23 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {
auto collection = Parameter<JSWeakCollection>(Descriptor::kCollection);
auto key = Parameter<Object>(Descriptor::kKey);
Label call_runtime(this), if_not_found(this);
Label call_runtime(this), if_cannot_be_weak_key(this);
GotoIfNotJSReceiver(key, &if_not_found);
GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);
TNode<IntPtrT> hash = LoadJSReceiverIdentityHash(CAST(key), &if_not_found);
TNode<IntPtrT> hash =
LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
TNode<EphemeronHashTable> table = LoadTable(collection);
TNode<IntPtrT> capacity = LoadTableCapacity(table);
TNode<IntPtrT> key_index =
FindKeyIndexForKey(table, key, hash, EntryMask(capacity), &if_not_found);
TNode<IntPtrT> key_index = FindKeyIndexForKey(
table, key, hash, EntryMask(capacity), &if_cannot_be_weak_key);
TNode<IntPtrT> number_of_elements = LoadNumberOfElements(table, -1);
GotoIf(ShouldShrink(capacity, number_of_elements), &call_runtime);
RemoveEntry(table, key_index, number_of_elements);
Return(TrueConstant());
BIND(&if_not_found);
BIND(&if_cannot_be_weak_key);
Return(FalseConstant());
BIND(&call_runtime);
@ -2884,7 +2891,7 @@ TF_BUILTIN(WeakMapPrototypeSet, WeakCollectionsBuiltinsAssembler) {
"WeakMap.prototype.set");
Label throw_invalid_key(this);
GotoIfNotJSReceiver(key, &throw_invalid_key);
GotoIfCannotBeWeakKey(key, &throw_invalid_key);
Return(
CallBuiltin(Builtin::kWeakCollectionSet, context, receiver, key, value));
@ -2902,7 +2909,7 @@ TF_BUILTIN(WeakSetPrototypeAdd, WeakCollectionsBuiltinsAssembler) {
"WeakSet.prototype.add");
Label throw_invalid_value(this);
GotoIfNotJSReceiver(value, &throw_invalid_value);
GotoIfCannotBeWeakKey(value, &throw_invalid_value);
Return(CallBuiltin(Builtin::kWeakCollectionSet, context, receiver, value,
TrueConstant()));

View File

@ -985,6 +985,10 @@ namespace internal {
TFS(WeakCollectionDelete, kCollection, kKey) \
TFS(WeakCollectionSet, kCollection, kKey, kValue) \
\
/* JS Structs */ \
CPP(SharedStructTypeConstructor) \
CPP(SharedStructConstructor) \
\
/* AsyncGenerator */ \
\
TFS(AsyncGeneratorResolve, kGenerator, kValue, kDone) \

View File

@ -0,0 +1,123 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-utils-inl.h"
#include "src/objects/js-struct-inl.h"
namespace v8 {
namespace internal {
constexpr int kMaxJSStructFields = 999;
#ifdef V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-limits.h"
static_assert(wasm::kV8MaxWasmStructFields == kMaxJSStructFields,
"Max number of fields should be the same for both JS and "
"WebAssembly structs");
#endif // V8_ENABLE_WEBASSEMBLY
BUILTIN(SharedStructTypeConstructor) {
DCHECK(FLAG_shared_string_table);
HandleScope scope(isolate);
static const char method_name[] = "SharedStructType";
auto* factory = isolate->factory();
Handle<JSReceiver> field_names_arg;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, field_names_arg,
Object::ToObject(isolate, args.atOrUndefined(isolate, 1), method_name));
// Treat field_names_arg as arraylike.
Handle<Object> raw_length_number;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, raw_length_number,
Object::GetLengthFromArrayLike(isolate, field_names_arg));
double num_properties_double = raw_length_number->Number();
if (num_properties_double < 0 || num_properties_double > kMaxJSStructFields) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kStructFieldCountOutOfRange));
}
int num_properties = static_cast<int>(num_properties_double);
Handle<DescriptorArray> descriptors = factory->NewDescriptorArray(
num_properties, 0, AllocationType::kSharedOld);
// Build up the descriptor array.
for (int i = 0; i < num_properties; ++i) {
Handle<Object> raw_field_name;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, raw_field_name,
JSReceiver::GetElement(isolate, field_names_arg, i));
Handle<Name> field_name;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, field_name,
Object::ToName(isolate, raw_field_name));
field_name = factory->InternalizeName(field_name);
// Shared structs' fields need to be aligned, so make it all tagged.
PropertyDetails details(
PropertyKind::kData, SEALED, PropertyLocation::kField,
PropertyConstness::kMutable, Representation::Tagged(), i);
descriptors->Set(InternalIndex(i), *field_name,
MaybeObject::FromObject(FieldType::Any()), details);
}
descriptors->Sort();
Handle<SharedFunctionInfo> info =
isolate->factory()->NewSharedFunctionInfoForBuiltin(
isolate->factory()->empty_string(), Builtin::kSharedStructConstructor,
FunctionKind::kNormalFunction);
info->set_internal_formal_parameter_count(JSParameterCount(0));
info->set_length(0);
Handle<JSFunction> constructor =
Factory::JSFunctionBuilder{isolate, info, isolate->native_context()}
.set_map(isolate->strict_function_map())
.Build();
int instance_size;
int in_object_properties;
JSFunction::CalculateInstanceSizeHelper(JS_SHARED_STRUCT_TYPE, false, 0,
num_properties, &instance_size,
&in_object_properties);
Handle<Map> instance_map = factory->NewMap(
JS_SHARED_STRUCT_TYPE, instance_size, TERMINAL_FAST_ELEMENTS_KIND,
in_object_properties, AllocationType::kSharedMap);
instance_map->InitializeDescriptors(isolate, *descriptors);
// Structs have fixed layout ahead of time, so there's no slack.
instance_map->SetInObjectUnusedPropertyFields(0);
instance_map->set_is_extensible(false);
JSFunction::SetInitialMap(isolate, constructor, instance_map,
factory->null_value());
// The constructor is not a shared object, so the shared map should not point
// to it.
instance_map->set_constructor_or_back_pointer(*factory->null_value());
return *constructor;
}
BUILTIN(SharedStructConstructor) {
HandleScope scope(isolate);
auto* factory = isolate->factory();
Handle<JSObject> instance =
factory->NewJSObject(args.target(), AllocationType::kSharedOld);
Handle<Map> instance_map(instance->map(), isolate);
if (instance_map->HasOutOfObjectProperties()) {
int num_oob_fields =
instance_map->NumberOfFields(ConcurrencyMode::kNotConcurrent) -
instance_map->GetInObjectProperties();
Handle<PropertyArray> property_array =
factory->NewPropertyArray(num_oob_fields, AllocationType::kSharedOld);
instance->SetProperties(*property_array);
}
return *instance;
}
} // namespace internal
} // namespace v8

View File

@ -3042,6 +3042,25 @@ void CodeStubAssembler::UnsafeStoreObjectFieldNoWriteBarrier(
object, offset, value);
}
void CodeStubAssembler::StoreJSSharedStructInObjectField(
TNode<HeapObject> object, TNode<IntPtrT> offset, TNode<Object> value) {
CSA_DCHECK(this, IsJSSharedStruct(object));
// JSSharedStructs are allocated in the shared old space, which is currently
// collected by stopping the world, so the incremental write barrier is not
// needed. They can only store Smis and other HeapObjects in the shared old
// space, so the generational write barrier is also not needed.
// TODO(v8:12547): Add a safer, shared variant of NoWriteBarrier instead of
// using Unsafe.
int const_offset;
if (TryToInt32Constant(offset, &const_offset)) {
UnsafeStoreObjectFieldNoWriteBarrier(object, const_offset, value);
} else {
UnsafeStoreNoWriteBarrier(MachineRepresentation::kTagged, object,
IntPtrSub(offset, IntPtrConstant(kHeapObjectTag)),
value);
}
}
void CodeStubAssembler::StoreMap(TNode<HeapObject> object, TNode<Map> map) {
OptimizedStoreMap(object, map);
DcheckHasValidMap(object);
@ -6540,6 +6559,19 @@ TNode<BoolT> CodeStubAssembler::IsJSArrayIterator(TNode<HeapObject> object) {
return HasInstanceType(object, JS_ARRAY_ITERATOR_TYPE);
}
TNode<BoolT> CodeStubAssembler::IsJSSharedStructInstanceType(
TNode<Int32T> instance_type) {
return InstanceTypeEqual(instance_type, JS_SHARED_STRUCT_TYPE);
}
TNode<BoolT> CodeStubAssembler::IsJSSharedStructMap(TNode<Map> map) {
return IsJSSharedStructInstanceType(LoadMapInstanceType(map));
}
TNode<BoolT> CodeStubAssembler::IsJSSharedStruct(TNode<HeapObject> object) {
return IsJSSharedStructMap(LoadMap(object));
}
TNode<BoolT> CodeStubAssembler::IsJSAsyncGeneratorObject(
TNode<HeapObject> object) {
return HasInstanceType(object, JS_ASYNC_GENERATOR_OBJECT_TYPE);
@ -6668,6 +6700,19 @@ TNode<BoolT> CodeStubAssembler::IsInternalizedStringInstanceType(
Int32Constant(kStringTag | kInternalizedTag));
}
TNode<BoolT> CodeStubAssembler::IsSharedStringInstanceType(
TNode<Int32T> instance_type) {
TNode<BoolT> is_shared = Word32Equal(
Word32And(instance_type,
Int32Constant(kIsNotStringMask | kSharedStringMask)),
Int32Constant(kStringTag | kSharedStringTag));
// TODO(v8:12007): Internalized strings do not have kSharedStringTag until
// the shared string table ships.
return Word32Or(is_shared,
Word32And(HasSharedStringTableFlag(),
IsInternalizedStringInstanceType(instance_type)));
}
TNode<BoolT> CodeStubAssembler::IsUniqueName(TNode<HeapObject> object) {
TNode<Uint16T> instance_type = LoadInstanceType(object);
return Select<BoolT>(
@ -6941,6 +6986,17 @@ TNode<BoolT> CodeStubAssembler::IsNumberArrayIndex(TNode<Number> number) {
[=] { return IsHeapNumberUint32(CAST(number)); });
}
TNode<BoolT> CodeStubAssembler::IsReadOnlyHeapObject(TNode<HeapObject> object) {
TNode<IntPtrT> object_word = BitcastTaggedToWord(object);
TNode<IntPtrT> page = PageFromAddress(object_word);
TNode<IntPtrT> flags = UncheckedCast<IntPtrT>(
Load(MachineType::Pointer(), page,
IntPtrConstant(BasicMemoryChunk::kFlagsOffset)));
return WordNotEqual(
WordAnd(flags, IntPtrConstant(BasicMemoryChunk::READ_ONLY_HEAP)),
IntPtrConstant(0));
}
template <typename TIndex>
TNode<BoolT> CodeStubAssembler::FixedArraySizeDoesntFitInNewSpace(
TNode<TIndex> element_count, int base_size) {
@ -15794,5 +15850,35 @@ void CodeStubAssembler::SwissNameDictionaryAdd(TNode<SwissNameDictionary> table,
}
}
void CodeStubAssembler::SharedValueBarrier(
TNode<Context> context, TNode<Object> value,
TVariable<Object>* var_shared_value) {
// The barrier ensures that the value can be shared across Isolates.
// The fast paths should be kept in sync with Object::Share.
Label skip_barrier(this);
// Fast path: Smis are trivially shared.
GotoIf(TaggedIsSmi(value), &skip_barrier);
// Fast path: Shared memory features imply shared RO space, so RO objects are
// trivially shared.
DCHECK(ReadOnlyHeap::IsReadOnlySpaceShared());
GotoIf(IsReadOnlyHeapObject(CAST(value)), &skip_barrier);
// Fast path: Check if the HeapObject is already shared.
TNode<Uint16T> value_instance_type =
LoadMapInstanceType(LoadMap(CAST(value)));
GotoIf(IsSharedStringInstanceType(value_instance_type), &skip_barrier);
GotoIf(IsJSSharedStructInstanceType(value_instance_type), &skip_barrier);
// Slow path: Call out to runtime to share primitives and to throw on
// non-shared JS objects.
*var_shared_value =
CallRuntime(Runtime::kSharedValueBarrierSlow, context, value);
Goto(&skip_barrier);
BIND(&skip_barrier);
}
} // namespace internal
} // namespace v8

View File

@ -1807,6 +1807,22 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
int additional_offset = 0);
void StoreJSSharedStructInObjectField(TNode<HeapObject> object,
TNode<IntPtrT> offset,
TNode<Object> value);
void StoreJSSharedStructPropertyArrayElement(TNode<PropertyArray> array,
TNode<IntPtrT> index,
TNode<Object> value) {
// JSSharedStructs are allocated in the shared old space, which is currently
// collected by stopping the world, so the incremental write barrier is not
// needed. They can only store Smis and other HeapObjects in the shared old
// space, so the generational write barrier is also not needed.
// TODO(v8:12547): Add a safer, shared variant of SKIP_WRITE_BARRIER.
StoreFixedArrayOrPropertyArrayElement(array, index, value,
UNSAFE_SKIP_WRITE_BARRIER);
}
// EnsureArrayPushable verifies that receiver with this map is:
// 1. Is not a prototype.
// 2. Is not a dictionary.
@ -2427,6 +2443,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TVariable<Numeric>* var_numeric,
TVariable<Smi>* var_feedback);
// Ensures that {value} is shareable across Isolates, and throws if not.
void SharedValueBarrier(TNode<Context> context, TNode<Object> value,
TVariable<Object>* var_shared_value);
TNode<WordT> TimesSystemPointerSize(TNode<WordT> value);
TNode<IntPtrT> TimesSystemPointerSize(TNode<IntPtrT> value) {
return Signed(TimesSystemPointerSize(implicit_cast<TNode<WordT>>(value)));
@ -2570,6 +2590,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsJSPrimitiveWrapperInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsJSPrimitiveWrapperMap(TNode<Map> map);
TNode<BoolT> IsJSPrimitiveWrapper(TNode<HeapObject> object);
TNode<BoolT> IsJSSharedStructInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsJSSharedStructMap(TNode<Map> map);
TNode<BoolT> IsJSSharedStruct(TNode<HeapObject> object);
TNode<BoolT> IsMap(TNode<HeapObject> object);
TNode<BoolT> IsName(TNode<HeapObject> object);
TNode<BoolT> IsNameInstanceType(TNode<Int32T> instance_type);
@ -2609,6 +2632,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsSymbolInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsInternalizedStringInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsSharedStringInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsTemporalInstantInstanceType(TNode<Int32T> instance_type);
TNode<BoolT> IsUniqueName(TNode<HeapObject> object);
TNode<BoolT> IsUniqueNameNoIndex(TNode<HeapObject> object);
@ -2616,6 +2640,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsUndetectableMap(TNode<Map> map);
TNode<BoolT> IsNotWeakFixedArraySubclass(TNode<HeapObject> object);
TNode<BoolT> IsZeroOrContext(TNode<Object> object);
TNode<BoolT> IsReadOnlyHeapObject(TNode<HeapObject> object);
TNode<BoolT> IsPromiseResolveProtectorCellInvalid();
TNode<BoolT> IsPromiseThenProtectorCellInvalid();
@ -2642,6 +2667,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
ExternalReference::address_of_builtin_subclassing_flag());
}
TNode<BoolT> HasSharedStringTableFlag() {
return LoadRuntimeFlag(
ExternalReference::address_of_shared_string_table_flag());
}
// True iff |object| is a Smi or a HeapNumber or a BigInt.
TNode<BoolT> IsNumeric(TNode<Object> object);

View File

@ -558,6 +558,10 @@ ExternalReference ExternalReference::address_of_runtime_stats_flag() {
return ExternalReference(&TracingFlags::runtime_stats);
}
ExternalReference ExternalReference::address_of_shared_string_table_flag() {
return ExternalReference(&FLAG_shared_string_table);
}
ExternalReference ExternalReference::address_of_load_from_stack_count(
const char* function_name) {
return ExternalReference(

View File

@ -104,6 +104,7 @@ class StatsCounter;
"FLAG_mock_arraybuffer_allocator") \
V(address_of_one_half, "LDoubleConstant::one_half") \
V(address_of_runtime_stats_flag, "TracingFlags::runtime_stats") \
V(address_of_shared_string_table_flag, "FLAG_shared_string_table") \
V(address_of_the_hole_nan, "the_hole_nan") \
V(address_of_uint32_bias, "uint32_bias") \
V(baseline_pc_for_bytecode_offset, "BaselinePCForBytecodeOffset") \

View File

@ -60,6 +60,7 @@ namespace internal {
"CallSite expects wasm object as first or function as second argument, " \
"got <%, %>") \
T(CallSiteMethod, "CallSite method % expects CallSite as receiver") \
T(CannotBeShared, "% cannot be shared") \
T(CannotConvertToPrimitive, "Cannot convert object to primitive value") \
T(CannotPreventExt, "Cannot prevent extensions") \
T(CannotFreeze, "Cannot freeze") \
@ -385,6 +386,8 @@ namespace internal {
T(ToPrecisionFormatRange, \
"toPrecision() argument must be between 1 and 100") \
T(ToRadixFormatRange, "toString() radix argument must be between 2 and 36") \
T(StructFieldCountOutOfRange, \
"Struct field count out of range (maximum of 999 allowed)") \
T(TypedArraySetOffsetOutOfBounds, "offset is out of bounds") \
T(TypedArraySetSourceTooLarge, "Source is too large") \
T(TypedArrayTooLargeToSort, \

View File

@ -1162,6 +1162,14 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
ZoneVector<PropertyAccessInfo> access_infos_for_feedback(zone());
for (const MapRef& map : inferred_maps) {
if (map.is_deprecated()) continue;
// TODO(v8:12547): Support writing to shared structs, which needs a write
// barrier that calls Object::Share to ensure the RHS is shared.
if (InstanceTypeChecker::IsJSSharedStruct(map.instance_type()) &&
access_mode == AccessMode::kStore) {
return NoChange();
}
PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
map, feedback.name(), access_mode, dependencies());
access_infos_for_feedback.push_back(access_info);
@ -1732,6 +1740,13 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
&prototype_maps)) {
return NoChange();
}
// TODO(v8:12547): Support writing to shared structs, which needs a
// write barrier that calls Object::Share to ensure the RHS is shared.
if (InstanceTypeChecker::IsJSSharedStruct(
receiver_map.instance_type())) {
return NoChange();
}
}
}
for (MapRef const& prototype_map : prototype_maps) {

View File

@ -258,6 +258,7 @@ Type::bitset BitsetType::Lub(const MapRefLike& map) {
case JS_WEAK_SET_TYPE:
case JS_PROMISE_TYPE:
case JS_SHADOW_REALM_TYPE:
case JS_SHARED_STRUCT_TYPE:
case JS_TEMPORAL_CALENDAR_TYPE:
case JS_TEMPORAL_DURATION_TYPE:
case JS_TEMPORAL_INSTANT_TYPE:

View File

@ -5148,7 +5148,9 @@ void Shell::WaitForRunningWorkers() {
namespace {
bool HasFlagThatRequiresSharedIsolate() { return i::FLAG_shared_string_table; }
bool HasFlagThatRequiresSharedIsolate() {
return i::FLAG_shared_string_table || i::FLAG_harmony_struct;
}
} // namespace

View File

@ -61,6 +61,7 @@
#include "src/objects/js-segmenter-inl.h"
#include "src/objects/js-segments-inl.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-struct-inl.h"
#include "src/objects/js-temporal-objects-inl.h"
#include "src/objects/js-weak-refs-inl.h"
#include "src/objects/literal-objects-inl.h"
@ -98,7 +99,7 @@ namespace internal {
// every encountered tagged pointer.
// - Verification should be pushed down to the specific instance type if its
// integrity is independent of an outer object.
// - In cases where the InstanceType is too genernic (e.g. FixedArray) the
// - In cases where the InstanceType is too generic (e.g. FixedArray) the
// XXXVerify of the outer method has to do recursive verification.
// - If the corresponding objects have inheritence the parent's Verify method
// is called as well.
@ -1182,6 +1183,33 @@ void JSMapIterator::JSMapIteratorVerify(Isolate* isolate) {
USE_TORQUE_VERIFIER(JSShadowRealm)
void JSSharedStruct::JSSharedStructVerify(Isolate* isolate) {
CHECK(IsJSSharedStruct());
JSObjectVerify(isolate);
CHECK(HasFastProperties());
// Shared structs can only point to primitives or other shared HeapObjects,
// even internally.
// TODO(v8:12547): Generalize shared -> shared pointer verification.
Map struct_map = map();
CHECK(struct_map.InSharedHeap());
CHECK(struct_map.GetBackPointer().IsUndefined(isolate));
Object maybe_cell = struct_map.prototype_validity_cell();
if (maybe_cell.IsCell()) CHECK(maybe_cell.InSharedHeap());
CHECK(!struct_map.is_extensible());
CHECK(!struct_map.is_prototype_map());
CHECK(property_array().InSharedHeap());
DescriptorArray descriptors = struct_map.instance_descriptors(isolate);
CHECK(descriptors.InSharedHeap());
for (InternalIndex i : struct_map.IterateOwnDescriptors()) {
PropertyDetails details = descriptors.GetDetails(i);
CHECK_EQ(PropertyKind::kData, details.kind());
CHECK_EQ(PropertyLocation::kField, details.location());
CHECK(details.representation().IsTagged());
CHECK(
RawFastPropertyAt(FieldIndex::ForDescriptor(struct_map, i)).IsShared());
}
}
void WeakCell::WeakCellVerify(Isolate* isolate) {
CHECK(IsWeakCell());

View File

@ -1425,6 +1425,14 @@ void JSFinalizationRegistry::JSFinalizationRegistryPrint(std::ostream& os) {
JSObjectPrintBody(os, *this);
}
void JSSharedStruct::JSSharedStructPrint(std::ostream& os) {
JSObjectPrintHeader(os, *this, "JSSharedStruct");
Isolate* isolate = GetIsolateFromWritableObject(*this);
os << "\n - isolate: " << isolate;
if (isolate->is_shared()) os << " (shared)";
JSObjectPrintBody(os, *this);
}
void JSWeakMap::JSWeakMapPrint(std::ostream& os) {
JSObjectPrintHeader(os, *this, "JSWeakMap");
os << "\n - table: " << Brief(table());

View File

@ -308,7 +308,8 @@ DEFINE_BOOL(harmony_shipping, true, "enable all shipped harmony features")
"harmony ResizableArrayBuffer / GrowableSharedArrayBuffer") \
V(harmony_temporal, "Temporal") \
V(harmony_shadow_realm, "harmony ShadowRealm") \
V(harmony_array_grouping, "harmony array grouping")
V(harmony_array_grouping, "harmony array grouping") \
V(harmony_struct, "harmony structs and shared structs")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \
@ -716,6 +717,7 @@ DEFINE_BOOL(trace_baseline_concurrent_compilation, false,
// Internalize into a shared string table in the shared isolate
DEFINE_BOOL(shared_string_table, false, "internalize strings into shared table")
DEFINE_IMPLICATION(harmony_struct, shared_string_table)
#if !defined(V8_OS_MACOSX) || !defined(V8_HOST_ARCH_ARM64)
DEFINE_BOOL(write_code_using_rwx, true,

View File

@ -47,6 +47,8 @@ template V8_EXPORT_PRIVATE Handle<HeapNumber>
FactoryBase<Factory>::NewHeapNumber<AllocationType::kOld>();
template V8_EXPORT_PRIVATE Handle<HeapNumber>
FactoryBase<Factory>::NewHeapNumber<AllocationType::kReadOnly>();
template V8_EXPORT_PRIVATE Handle<HeapNumber>
FactoryBase<Factory>::NewHeapNumber<AllocationType::kSharedOld>();
template V8_EXPORT_PRIVATE Handle<HeapNumber>
FactoryBase<LocalFactory>::NewHeapNumber<AllocationType::kOld>();

View File

@ -444,10 +444,11 @@ Handle<Oddball> Factory::NewBasicBlockCountersMarker() {
Oddball::kBasicBlockCountersMarker);
}
Handle<PropertyArray> Factory::NewPropertyArray(int length) {
Handle<PropertyArray> Factory::NewPropertyArray(int length,
AllocationType allocation) {
DCHECK_LE(0, length);
if (length == 0) return empty_property_array();
HeapObject result = AllocateRawFixedArray(length, AllocationType::kYoung);
HeapObject result = AllocateRawFixedArray(length, allocation);
DisallowGarbageCollection no_gc;
result.set_map_after_allocation(*property_array_map(), SKIP_WRITE_BARRIER);
PropertyArray array = PropertyArray::cast(result);
@ -1850,15 +1851,19 @@ Handle<Map> Factory::NewMap(InstanceType type, int instance_size,
HeapObject result = isolate()->heap()->AllocateRawWith<Heap::kRetryOrFail>(
Map::kSize, allocation_type);
DisallowGarbageCollection no_gc;
result.set_map_after_allocation(*meta_map(), SKIP_WRITE_BARRIER);
Heap* roots = allocation_type == AllocationType::kMap
? isolate()->heap()
: isolate()->shared_isolate()->heap();
result.set_map_after_allocation(ReadOnlyRoots(roots).meta_map(),
SKIP_WRITE_BARRIER);
return handle(InitializeMap(Map::cast(result), type, instance_size,
elements_kind, inobject_properties),
elements_kind, inobject_properties, roots),
isolate());
}
Map Factory::InitializeMap(Map map, InstanceType type, int instance_size,
ElementsKind elements_kind,
int inobject_properties) {
ElementsKind elements_kind, int inobject_properties,
Heap* roots) {
DisallowGarbageCollection no_gc;
map.set_bit_field(0);
map.set_bit_field2(Map::Bits2::NewTargetIsBaseBit::encode(true));
@ -1869,7 +1874,8 @@ Map Factory::InitializeMap(Map map, InstanceType type, int instance_size,
Map::Bits3::IsExtensibleBit::encode(true);
map.set_bit_field3(bit_field3);
map.set_instance_type(type);
HeapObject raw_null_value = *null_value();
ReadOnlyRoots ro_roots(roots);
HeapObject raw_null_value = ro_roots.null_value();
map.set_prototype(raw_null_value, SKIP_WRITE_BARRIER);
map.set_constructor_or_back_pointer(raw_null_value, SKIP_WRITE_BARRIER);
map.set_instance_size(instance_size);
@ -1878,20 +1884,19 @@ Map Factory::InitializeMap(Map map, InstanceType type, int instance_size,
map.SetInObjectPropertiesStartInWords(instance_size / kTaggedSize -
inobject_properties);
DCHECK_EQ(map.GetInObjectProperties(), inobject_properties);
map.set_prototype_validity_cell(*invalid_prototype_validity_cell());
map.set_prototype_validity_cell(roots->invalid_prototype_validity_cell());
} else {
DCHECK_EQ(inobject_properties, 0);
map.set_inobject_properties_start_or_constructor_function_index(0);
map.set_prototype_validity_cell(Smi::FromInt(Map::kPrototypeChainValid),
SKIP_WRITE_BARRIER);
}
map.set_dependent_code(
DependentCode::empty_dependent_code(ReadOnlyRoots(isolate())),
SKIP_WRITE_BARRIER);
map.set_dependent_code(DependentCode::empty_dependent_code(ro_roots),
SKIP_WRITE_BARRIER);
map.set_raw_transitions(MaybeObject::FromSmi(Smi::zero()),
SKIP_WRITE_BARRIER);
map.SetInObjectUnusedPropertyFields(inobject_properties);
map.SetInstanceDescriptors(isolate(), *empty_descriptor_array(), 0);
map.SetInstanceDescriptors(isolate(), ro_roots.empty_descriptor_array(), 0);
// Must be called only after |instance_type| and |instance_size| are set.
map.set_visitor_id(Map::GetVisitorId(map));
DCHECK(!map.is_in_retained_map_list());

View File

@ -128,7 +128,8 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<Oddball> NewBasicBlockCountersMarker();
// Allocates a property array initialized with undefined values.
Handle<PropertyArray> NewPropertyArray(int length);
Handle<PropertyArray> NewPropertyArray(
int length, AllocationType allocation = AllocationType::kYoung);
// Tries allocating a fixed array initialized with undefined values.
// In case of an allocation failure (OOM) an empty handle is returned.
// The caller has to manually signal an
@ -445,10 +446,12 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND,
int inobject_properties = 0,
AllocationType allocation_type = AllocationType::kMap);
// Initializes the fields of a newly created Map. Exposed for tests and
// heap setup; other code should just call NewMap which takes care of it.
// Initializes the fields of a newly created Map using roots from the
// passed-in Heap. Exposed for tests and heap setup; other code should just
// call NewMap which takes care of it.
Map InitializeMap(Map map, InstanceType type, int instance_size,
ElementsKind elements_kind, int inobject_properties);
ElementsKind elements_kind, int inobject_properties,
Heap* roots);
// Allocate a block of memory of the given AllocationType (filled with a
// filler). Used as a fall-back for generated code when the space is full.

View File

@ -6364,7 +6364,7 @@ void Heap::CompactWeakArrayLists() {
}
void Heap::AddRetainedMap(Handle<NativeContext> context, Handle<Map> map) {
if (map->is_in_retained_map_list()) {
if (map->is_in_retained_map_list() || map->InSharedWritableHeap()) {
return;
}

View File

@ -422,7 +422,7 @@ void IncrementalMarking::FinalizeIncrementally() {
// 2) Age and retain maps embedded in optimized code.
MarkRoots();
// Map retaining is needed for perfromance, not correctness,
// Map retaining is needed for performance, not correctness,
// so we can do it only once at the beginning of the finalization.
RetainMaps();

View File

@ -151,7 +151,7 @@ AllocationResult Heap::AllocateMap(InstanceType instance_type,
SKIP_WRITE_BARRIER);
Map map = isolate()->factory()->InitializeMap(
Map::cast(result), instance_type, instance_size, elements_kind,
inobject_properties);
inobject_properties, this);
return AllocationResult::FromObject(map);
}

View File

@ -1186,6 +1186,37 @@ void AccessorAssembler::HandleStoreICNativeDataProperty(
holder, accessor_info, p->name(), p->value());
}
void AccessorAssembler::HandleStoreICSmiHandlerJSSharedStructFieldCase(
TNode<Context> context, TNode<Word32T> handler_word, TNode<JSObject> holder,
TNode<Object> value) {
CSA_DCHECK(this,
Word32Equal(DecodeWord32<StoreHandler::KindBits>(handler_word),
STORE_KIND(kSharedStructField)));
CSA_DCHECK(
this,
Word32Equal(DecodeWord32<StoreHandler::RepresentationBits>(handler_word),
Int32Constant(Representation::kTagged)));
TVARIABLE(Object, shared_value, value);
SharedValueBarrier(context, value, &shared_value);
TNode<BoolT> is_inobject =
IsSetWord32<StoreHandler::IsInobjectBits>(handler_word);
TNode<HeapObject> property_storage = Select<HeapObject>(
is_inobject, [&]() { return holder; },
[&]() { return LoadFastProperties(holder); });
TNode<UintPtrT> index =
DecodeWordFromWord32<StoreHandler::FieldIndexBits>(handler_word);
TNode<IntPtrT> offset = Signed(TimesTaggedSize(index));
StoreJSSharedStructInObjectField(property_storage, offset,
shared_value.value());
// Return the original value.
Return(value);
}
void AccessorAssembler::HandleStoreICHandlerCase(
const StoreICParameters* p, TNode<MaybeObject> handler, Label* miss,
ICMode ic_mode, ElementSupport support_elements) {
@ -1269,10 +1300,13 @@ void AccessorAssembler::HandleStoreICHandlerCase(
BIND(&if_fast_smi);
{
Label data(this), accessor(this), native_data_property(this);
Label data(this), accessor(this), shared_struct_field(this),
native_data_property(this);
GotoIf(Word32Equal(handler_kind, STORE_KIND(kAccessor)), &accessor);
Branch(Word32Equal(handler_kind, STORE_KIND(kNativeDataProperty)),
&native_data_property, &data);
GotoIf(Word32Equal(handler_kind, STORE_KIND(kNativeDataProperty)),
&native_data_property);
Branch(Word32Equal(handler_kind, STORE_KIND(kSharedStructField)),
&shared_struct_field, &data);
BIND(&accessor);
HandleStoreAccessor(p, CAST(holder), handler_word);
@ -1280,6 +1314,10 @@ void AccessorAssembler::HandleStoreICHandlerCase(
BIND(&native_data_property);
HandleStoreICNativeDataProperty(p, CAST(holder), handler_word);
BIND(&shared_struct_field);
HandleStoreICSmiHandlerJSSharedStructFieldCase(p->context(), handler_word,
CAST(holder), p->value());
BIND(&data);
// Handle non-transitioning field stores.
HandleStoreICSmiHandlerCase(handler_word, CAST(holder), p->value(), miss);
@ -1663,6 +1701,55 @@ void AccessorAssembler::OverwriteExistingFastDataProperty(
BIND(&done);
}
void AccessorAssembler::StoreJSSharedStructField(
TNode<Context> context, TNode<HeapObject> shared_struct,
TNode<Map> shared_struct_map, TNode<DescriptorArray> descriptors,
TNode<IntPtrT> descriptor_name_index, TNode<Uint32T> details,
TNode<Object> maybe_local_value) {
CSA_DCHECK(this, IsJSSharedStruct(shared_struct));
Label done(this);
TNode<UintPtrT> field_index =
DecodeWordFromWord32<PropertyDetails::FieldIndexField>(details);
field_index = Unsigned(IntPtrAdd(
field_index,
Unsigned(LoadMapInobjectPropertiesStartInWords(shared_struct_map))));
TNode<IntPtrT> instance_size_in_words =
LoadMapInstanceSizeInWords(shared_struct_map);
TVARIABLE(Object, shared_value, maybe_local_value);
SharedValueBarrier(context, maybe_local_value, &shared_value);
Label inobject(this), backing_store(this);
Branch(UintPtrLessThan(field_index, instance_size_in_words), &inobject,
&backing_store);
BIND(&inobject);
{
TNode<IntPtrT> field_offset = Signed(TimesTaggedSize(field_index));
StoreJSSharedStructInObjectField(shared_struct, field_offset,
shared_value.value());
Goto(&done);
}
BIND(&backing_store);
{
TNode<IntPtrT> backing_store_index =
Signed(IntPtrSub(field_index, instance_size_in_words));
Label tagged_rep(this), double_rep(this);
TNode<PropertyArray> properties =
CAST(LoadFastProperties(CAST(shared_struct)));
StoreJSSharedStructPropertyArrayElement(properties, backing_store_index,
shared_value.value());
Goto(&done);
}
BIND(&done);
}
void AccessorAssembler::CheckPrototypeValidityCell(
TNode<Object> maybe_validity_cell, Label* miss) {
Label done(this);

View File

@ -273,6 +273,13 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
TNode<Object> value, Label* slow,
bool do_transitioning_store);
void StoreJSSharedStructField(TNode<Context> context,
TNode<HeapObject> shared_struct,
TNode<Map> shared_struct_map,
TNode<DescriptorArray> descriptors,
TNode<IntPtrT> descriptor_name_index,
TNode<Uint32T> details, TNode<Object> value);
TNode<BoolT> IsPropertyDetailsConst(TNode<Uint32T> details);
void CheckFieldType(TNode<DescriptorArray> descriptors,
@ -435,6 +442,9 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
void HandleStoreICSmiHandlerCase(TNode<Word32T> handler_word,
TNode<JSObject> holder, TNode<Object> value,
Label* miss);
void HandleStoreICSmiHandlerJSSharedStructFieldCase(
TNode<Context> context, TNode<Word32T> handler_word,
TNode<JSObject> holder, TNode<Object> value);
void HandleStoreFieldAndReturn(TNode<Word32T> handler_word,
TNode<JSObject> holder, TNode<Object> value,
base::Optional<TNode<Float64T>> double_value,

View File

@ -225,7 +225,8 @@ Handle<Smi> StoreHandler::StoreField(Isolate* isolate, Kind kind,
int descriptor, FieldIndex field_index,
Representation representation) {
DCHECK(!representation.IsNone());
DCHECK(kind == Kind::kField || kind == Kind::kConstField);
DCHECK(kind == Kind::kField || kind == Kind::kConstField ||
kind == Kind::kSharedStructField);
int config = KindBits::encode(kind) |
IsInobjectBits::encode(field_index.is_inobject()) |
@ -244,6 +245,14 @@ Handle<Smi> StoreHandler::StoreField(Isolate* isolate, int descriptor,
return StoreField(isolate, kind, descriptor, field_index, representation);
}
Handle<Smi> StoreHandler::StoreSharedStructField(
Isolate* isolate, int descriptor, FieldIndex field_index,
Representation representation) {
DCHECK(representation.Equals(Representation::Tagged()));
return StoreField(isolate, Kind::kSharedStructField, descriptor, field_index,
representation);
}
Handle<Smi> StoreHandler::StoreNativeDataProperty(Isolate* isolate,
int descriptor) {
int config = KindBits::encode(Kind::kNativeDataProperty) |

View File

@ -250,6 +250,7 @@ class StoreHandler final : public DataHandler {
kConstField,
kAccessor,
kNativeDataProperty,
kSharedStructField,
kApiSetter,
kApiSetterHolderIsPrototype,
kGlobalProxy,
@ -301,6 +302,11 @@ class StoreHandler final : public DataHandler {
PropertyConstness constness,
Representation representation);
// Creates a Smi-handler for storing a field to a JSSharedStruct.
static inline Handle<Smi> StoreSharedStructField(
Isolate* isolate, int descriptor, FieldIndex field_index,
Representation representation);
// Create a store transition handler which doesn't check prototype chain.
static MaybeObjectHandle StoreOwnTransition(Isolate* isolate,
Handle<Map> transition_map);

View File

@ -2147,6 +2147,10 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreFieldDH);
int descriptor = lookup->GetFieldDescriptorIndex();
FieldIndex index = lookup->GetFieldIndex();
if (V8_UNLIKELY(holder->IsJSSharedStruct())) {
return MaybeObjectHandle(StoreHandler::StoreSharedStructField(
isolate(), descriptor, index, lookup->representation()));
}
PropertyConstness constness = lookup->constness();
if (constness == PropertyConstness::kConst &&
IsStoreOwnICKind(nexus()->kind())) {

View File

@ -83,16 +83,18 @@ class KeyedStoreGenericAssembler : public AccessorAssembler {
// kind.
void EmitGenericPropertyStore(TNode<JSReceiver> receiver,
TNode<Map> receiver_map,
TNode<Uint16T> instance_type,
const StoreICParameters* p,
ExitPoint* exit_point, Label* slow,
Maybe<LanguageMode> maybe_language_mode);
void EmitGenericPropertyStore(TNode<JSReceiver> receiver,
TNode<Map> receiver_map,
TNode<Uint16T> instance_type,
const StoreICParameters* p, Label* slow) {
ExitPoint direct_exit(this);
EmitGenericPropertyStore(receiver, receiver_map, p, &direct_exit, slow,
Nothing<LanguageMode>());
EmitGenericPropertyStore(receiver, receiver_map, instance_type, p,
&direct_exit, slow, Nothing<LanguageMode>());
}
void BranchIfPrototypesMayHaveReadOnlyElements(
@ -792,7 +794,8 @@ TNode<Map> KeyedStoreGenericAssembler::FindCandidateStoreICTransitionMapHandler(
void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
TNode<JSReceiver> receiver, TNode<Map> receiver_map,
const StoreICParameters* p, ExitPoint* exit_point, Label* slow,
TNode<Uint16T> instance_type, const StoreICParameters* p,
ExitPoint* exit_point, Label* slow,
Maybe<LanguageMode> maybe_language_mode) {
CSA_DCHECK(this, IsSimpleObjectMap(receiver_map));
// TODO(rmcilroy) Type as Struct once we use a trimmed down
@ -841,11 +844,22 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
BIND(&data_property);
{
Label shared(this);
GotoIf(IsJSSharedStructInstanceType(instance_type), &shared);
CheckForAssociatedProtector(name, slow);
OverwriteExistingFastDataProperty(receiver, receiver_map, descriptors,
name_index, details, p->value(), slow,
false);
exit_point->Return(p->value());
BIND(&shared);
{
StoreJSSharedStructField(p->context(), receiver, receiver_map,
descriptors, name_index, details,
p->value());
exit_point->Return(p->value());
}
}
}
BIND(&lookup_transition);
@ -1068,8 +1082,8 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(
StoreICParameters p(context, receiver, var_unique.value(), value, {},
UndefinedConstant(), StoreICMode::kDefault);
ExitPoint direct_exit(this);
EmitGenericPropertyStore(CAST(receiver), receiver_map, &p, &direct_exit,
&slow, language_mode);
EmitGenericPropertyStore(CAST(receiver), receiver_map, instance_type, &p,
&direct_exit, &slow, language_mode);
}
BIND(&not_internalized);
@ -1143,7 +1157,8 @@ void KeyedStoreGenericAssembler::StoreIC_NoFeedback() {
StoreICParameters p(
context, receiver, name, value, slot, UndefinedConstant(),
IsKeyedStoreOwn() ? StoreICMode::kStoreOwn : StoreICMode::kDefault);
EmitGenericPropertyStore(CAST(receiver), receiver_map, &p, &miss);
EmitGenericPropertyStore(CAST(receiver), receiver_map, instance_type, &p,
&miss);
}
}
@ -1172,7 +1187,9 @@ void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context,
IsSimpleObjectMap(LoadMap(receiver))));
GotoIfNot(is_simple_receiver, &slow);
EmitGenericPropertyStore(receiver, LoadMap(receiver), &p, &exit_point, &slow,
TNode<Map> map = LoadMap(receiver);
TNode<Uint16T> instance_type = LoadMapInstanceType(map);
EmitGenericPropertyStore(receiver, map, instance_type, &p, &exit_point, &slow,
Just(language_mode));
BIND(&slow);

View File

@ -60,6 +60,7 @@
#include "src/objects/js-segments.h"
#endif // V8_INTL_SUPPORT
#include "src/codegen/script-details.h"
#include "src/objects/js-struct.h"
#include "src/objects/js-temporal-objects-inl.h"
#include "src/objects/js-weak-refs.h"
#include "src/objects/ordered-hash-table.h"
@ -4444,6 +4445,24 @@ void Genesis::InitializeGlobal_harmony_shadow_realm() {
Builtin::kShadowRealmPrototypeImportValue, 2, true);
}
void Genesis::InitializeGlobal_harmony_struct() {
if (!FLAG_harmony_struct) return;
Handle<JSGlobalObject> global(native_context()->global_object(), isolate());
Handle<String> name =
isolate()->factory()->InternalizeUtf8String("SharedStructType");
Handle<JSFunction> shared_struct_type_fun = CreateFunctionForBuiltin(
isolate(), name, isolate()->strict_function_with_readonly_prototype_map(),
Builtin::kSharedStructTypeConstructor);
JSObject::MakePrototypesFast(shared_struct_type_fun, kStartAtReceiver,
isolate());
shared_struct_type_fun->shared().set_native(true);
shared_struct_type_fun->shared().DontAdaptArguments();
shared_struct_type_fun->shared().set_length(1);
JSObject::AddProperty(isolate(), global, "SharedStructType",
shared_struct_type_fun, DONT_ENUM);
}
void Genesis::InitializeGlobal_harmony_array_find_last() {
if (!FLAG_harmony_array_find_last) return;

View File

@ -48,6 +48,7 @@
#include "src/objects/js-regexp-inl.h"
#include "src/objects/js-regexp-string-iterator-inl.h"
#include "src/objects/js-shadow-realms-inl.h"
#include "src/objects/js-struct-inl.h"
#include "src/objects/js-temporal-objects-inl.h"
#include "src/objects/js-weak-refs-inl.h"
#include "src/objects/literal-objects-inl.h"

View File

@ -334,7 +334,7 @@ Object JSObject::RawFastPropertyAt(FieldIndex index) const {
Object JSObject::RawFastPropertyAt(PtrComprCageBase cage_base,
FieldIndex index) const {
if (index.is_inobject()) {
return TaggedField<Object>::load(cage_base, *this, index.offset());
return TaggedField<Object>::Relaxed_Load(cage_base, *this, index.offset());
} else {
return property_array(cage_base).get(cage_base,
index.outobject_array_index());

View File

@ -59,6 +59,7 @@
#include "src/objects/js-segmenter.h"
#include "src/objects/js-segments.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-struct-inl.h"
#include "src/objects/js-temporal-objects-inl.h"
#include "src/objects/js-weak-refs.h"
#include "src/objects/map-inl.h"
@ -2342,6 +2343,8 @@ int JSObject::GetHeaderSize(InstanceType type,
return JSStringIterator::kHeaderSize;
case JS_MODULE_NAMESPACE_TYPE:
return JSModuleNamespace::kHeaderSize;
case JS_SHARED_STRUCT_TYPE:
return JSSharedStruct::kHeaderSize;
case JS_TEMPORAL_CALENDAR_TYPE:
return JSTemporalCalendar::kHeaderSize;
case JS_TEMPORAL_DURATION_TYPE:

View File

@ -0,0 +1,30 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_JS_STRUCT_INL_H_
#define V8_OBJECTS_JS_STRUCT_INL_H_
#include "src/api/api-inl.h"
#include "src/heap/heap-write-barrier-inl.h"
#include "src/objects/js-struct.h"
#include "src/objects/smi-inl.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
#include "torque-generated/src/objects/js-struct-tq-inl.inc"
TQ_OBJECT_CONSTRUCTORS_IMPL(JSSharedStruct)
CAST_ACCESSOR(JSSharedStruct)
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_STRUCT_INL_H_

35
src/objects/js-struct.h Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_JS_STRUCT_H_
#define V8_OBJECTS_JS_STRUCT_H_
#include "src/objects/js-objects.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
#include "torque-generated/src/objects/js-struct-tq.inc"
class JSSharedStruct
: public TorqueGeneratedJSSharedStruct<JSSharedStruct, JSObject> {
public:
DECL_CAST(JSSharedStruct)
DECL_PRINTER(JSSharedStruct)
EXPORT_DECL_VERIFIER(JSSharedStruct)
class BodyDescriptor;
TQ_OBJECT_CONSTRUCTORS(JSSharedStruct)
};
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_STRUCT_H_

7
src/objects/js-struct.tq Normal file
View File

@ -0,0 +1,7 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
extern class JSSharedStruct extends JSObject {
// escaped_local_thread: Smi;
}

View File

@ -1061,6 +1061,7 @@ void LookupIterator::WriteDataValue(Handle<Object> value,
// WasmObjects.
DCHECK(!holder_->IsWasmObject(isolate_));
#endif // V8_ENABLE_WEBASSEMBLY
DCHECK_IMPLIES(holder_->IsJSSharedStruct(), value->IsShared());
Handle<JSReceiver> holder = GetHolder<JSReceiver>();
if (IsElement(*holder)) {

View File

@ -273,6 +273,7 @@ VisitorId Map::GetVisitorId(Map map) {
case JS_SET_TYPE:
case JS_SET_VALUE_ITERATOR_TYPE:
case JS_SHADOW_REALM_TYPE:
case JS_SHARED_STRUCT_TYPE:
case JS_STRING_ITERATOR_PROTOTYPE_TYPE:
case JS_STRING_ITERATOR_TYPE:
case JS_TEMPORAL_CALENDAR_TYPE:

View File

@ -159,6 +159,7 @@ class ZoneForwardList;
V(JSSet) \
V(JSSetIterator) \
V(JSShadowRealm) \
V(JSSharedStruct) \
V(JSSpecialObject) \
V(JSStringIterator) \
V(JSTemporalCalendar) \

View File

@ -1157,6 +1157,7 @@ auto BodyDescriptorApply(InstanceType type, Args&&... args) {
case JS_SET_VALUE_ITERATOR_TYPE:
case JS_SPECIAL_API_OBJECT_TYPE:
case JS_SHADOW_REALM_TYPE:
case JS_SHARED_STRUCT_TYPE:
case JS_STRING_ITERATOR_PROTOTYPE_TYPE:
case JS_STRING_ITERATOR_TYPE:
case JS_TEMPORAL_CALENDAR_TYPE:

View File

@ -1164,6 +1164,46 @@ Object Object::GetHash() {
return receiver.GetIdentityHash();
}
bool Object::IsShared() const {
// Smis are trivially shared.
if (IsSmi()) return true;
HeapObject object = HeapObject::cast(*this);
// RO objects are shared when the RO space is shared.
if (IsReadOnlyHeapObject(object)) {
return ReadOnlyHeap::IsReadOnlySpaceShared();
}
// Check if this object is already shared.
switch (object.map().instance_type()) {
case SHARED_STRING_TYPE:
case SHARED_ONE_BYTE_STRING_TYPE:
case JS_SHARED_STRUCT_TYPE:
DCHECK(object.InSharedHeap());
return true;
case INTERNALIZED_STRING_TYPE:
case ONE_BYTE_INTERNALIZED_STRING_TYPE:
if (FLAG_shared_string_table) {
DCHECK(object.InSharedHeap());
return true;
}
return false;
default:
return false;
}
}
// static
MaybeHandle<Object> Object::Share(Isolate* isolate, Handle<Object> value,
ShouldThrow throw_if_cannot_be_shared) {
// Sharing values requires the RO space be shared.
DCHECK(ReadOnlyHeap::IsReadOnlySpaceShared());
if (value->IsShared()) return value;
return ShareSlow(isolate, Handle<HeapObject>::cast(value),
throw_if_cannot_be_shared);
}
Handle<Object> ObjectHashTableShape::AsHandle(Handle<Object> key) {
return key;
}

View File

@ -2823,7 +2823,16 @@ Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) {
} else // NOLINT(readability/braces)
#endif // V8_ENABLE_WEBASSEMBLY
{
// clang-format off
if (V8_UNLIKELY(receiver->IsJSSharedStruct(isolate))) {
// clang-format on
// Shared structs can only point to primitives or shared values.
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, to_assign, Object::Share(isolate, to_assign, kThrowOnError),
Nothing<bool>());
it->WriteDataValue(to_assign, false);
} else {
// Possibly migrate to the most up-to-date map that will be able to store
// |value| under it->name().
it->PrepareForDataProperty(to_assign);
@ -2915,6 +2924,30 @@ Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value,
return Just(true);
}
// static
MaybeHandle<Object> Object::ShareSlow(Isolate* isolate,
Handle<HeapObject> value,
ShouldThrow throw_if_cannot_be_shared) {
// Use Object::Share() if value might already be shared.
DCHECK(!value->IsShared());
if (value->IsString()) {
return String::Share(isolate, Handle<String>::cast(value));
}
if (value->IsHeapNumber()) {
uint64_t bits = HeapNumber::cast(*value).value_as_bits(kRelaxedLoad);
return isolate->factory()
->NewHeapNumberFromBits<AllocationType::kSharedOld>(bits);
}
if (throw_if_cannot_be_shared == kThrowOnError) {
THROW_NEW_ERROR(
isolate, NewTypeError(MessageTemplate::kCannotBeShared, value), Object);
}
return MaybeHandle<Object>();
}
template <class T>
static int AppendUniqueCallbacks(Isolate* isolate,
Handle<TemplateList> callbacks,

View File

@ -66,6 +66,7 @@
// - JSRegExp
// - JSSetIterator
// - JSShadowRealm
// - JSSharedStruct
// - JSStringIterator
// - JSTemporalCalendar
// - JSTemporalDuration
@ -729,6 +730,27 @@ class Object : public TaggedImpl<HeapObjectReferenceType::STRONG, Address> {
static bool CheckContextualStoreToJSGlobalObject(
LookupIterator* it, Maybe<ShouldThrow> should_throw);
// Returns whether the object is safe to share across Isolates.
//
// Currently, the following kinds of values can be safely shared across
// Isolates:
// - Smis
// - Objects in RO space when the RO space is shared
// - HeapNumbers in the shared old space
// - Strings for which String::IsShared() is true
// - JSSharedStructs
inline bool IsShared() const;
// Returns an equivalent value that's safe to share across Isolates if
// possible. Acts as the identity function when value->IsShared().
static inline MaybeHandle<Object> Share(
Isolate* isolate, Handle<Object> value,
ShouldThrow throw_if_cannot_be_shared);
static MaybeHandle<Object> ShareSlow(Isolate* isolate,
Handle<HeapObject> value,
ShouldThrow throw_if_cannot_be_shared);
protected:
inline Address field_address(size_t offset) const {
return ptr() + offset - kHeapObjectTag;

View File

@ -25,6 +25,7 @@
#include "src/objects/js-array-inl.h"
#include "src/objects/js-collection-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/js-struct-inl.h"
#include "src/objects/map-updater.h"
#include "src/objects/objects-inl.h"
#include "src/objects/objects.h"
@ -590,6 +591,8 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
return WriteJSArrayBufferView(JSArrayBufferView::cast(*receiver));
case JS_ERROR_TYPE:
return WriteJSError(Handle<JSObject>::cast(receiver));
case JS_SHARED_STRUCT_TYPE:
return WriteJSSharedStruct(Handle<JSSharedStruct>::cast(receiver));
#if V8_ENABLE_WEBASSEMBLY
case WASM_MODULE_OBJECT_TYPE:
return WriteWasmModule(Handle<WasmModuleObject>::cast(receiver));
@ -1025,6 +1028,12 @@ Maybe<bool> ValueSerializer::WriteJSError(Handle<JSObject> error) {
return ThrowIfOutOfMemory();
}
Maybe<bool> ValueSerializer::WriteJSSharedStruct(
Handle<JSSharedStruct> shared_struct) {
// TODO(v8:12547): Support copying serialization for shared structs as well.
return WriteSharedObject(shared_struct);
}
#if V8_ENABLE_WEBASSEMBLY
Maybe<bool> ValueSerializer::WriteWasmModule(Handle<WasmModuleObject> object) {
if (delegate_ == nullptr) {
@ -1061,8 +1070,7 @@ Maybe<bool> ValueSerializer::WriteWasmMemory(Handle<WasmMemoryObject> object) {
#endif // V8_ENABLE_WEBASSEMBLY
Maybe<bool> ValueSerializer::WriteSharedObject(Handle<HeapObject> object) {
// Currently only strings are shareable.
DCHECK(String::cast(*object).IsShared());
DCHECK(object->IsShared());
DCHECK(supports_shared_values_);
DCHECK_NOT_NULL(delegate_);
DCHECK(delegate_->SupportsSharedValues());
@ -2144,8 +2152,7 @@ MaybeHandle<HeapObject> ValueDeserializer::ReadSharedObject() {
}
Handle<HeapObject> shared_object =
Handle<HeapObject>::cast(Utils::OpenHandle(*shared_value));
// Currently only strings are shareable.
DCHECK(String::cast(*shared_object).IsShared());
DCHECK(shared_object->IsShared());
return shared_object;
}

View File

@ -30,6 +30,7 @@ class JSMap;
class JSPrimitiveWrapper;
class JSRegExp;
class JSSet;
class JSSharedStruct;
class Object;
class Oddball;
class Smi;
@ -131,6 +132,8 @@ class ValueSerializer {
V8_WARN_UNUSED_RESULT;
Maybe<bool> WriteJSArrayBufferView(JSArrayBufferView array_buffer);
Maybe<bool> WriteJSError(Handle<JSObject> error) V8_WARN_UNUSED_RESULT;
Maybe<bool> WriteJSSharedStruct(Handle<JSSharedStruct> shared_struct)
V8_WARN_UNUSED_RESULT;
#if V8_ENABLE_WEBASSEMBLY
Maybe<bool> WriteWasmModule(Handle<WasmModuleObject> object)
V8_WARN_UNUSED_RESULT;

View File

@ -737,5 +737,15 @@ RUNTIME_FUNCTION(Runtime_DoubleToStringWithRadix) {
return *result;
}
RUNTIME_FUNCTION(Runtime_SharedValueBarrierSlow) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(HeapObject, value, 0);
Handle<Object> shared_value;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, shared_value, Object::ShareSlow(isolate, value, kThrowOnError));
return *shared_value;
}
} // namespace internal
} // namespace v8

View File

@ -233,6 +233,7 @@ namespace internal {
F(ReThrowWithMessage, 2, 1) \
F(RunMicrotaskCallback, 2, 1) \
F(PerformMicrotaskCheckpoint, 0, 1) \
F(SharedValueBarrierSlow, 1, 1) \
F(StackGuard, 0, 1) \
F(StackGuardWithGap, 1, 1) \
F(Throw, 1, 1) \

View File

@ -84,7 +84,7 @@ AllocationResult HeapTester::AllocateMapForTest(Isolate* isolate) {
SKIP_WRITE_BARRIER);
return AllocationResult::FromObject(isolate->factory()->InitializeMap(
Map::cast(obj), JS_OBJECT_TYPE, JSObject::kHeaderSize,
TERMINAL_FAST_ELEMENTS_KIND, 0));
TERMINAL_FAST_ELEMENTS_KIND, 0, heap));
}
// This is the same as Factory::NewFixedArray, except it doesn't retry

View File

@ -83,7 +83,7 @@ bytecodes: [
/* 48 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 53 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 58 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(291),
B(Wide), B(LdaSmi), I16(293),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -115,7 +115,7 @@ bytecodes: [
/* 41 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 46 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 51 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(290),
B(Wide), B(LdaSmi), I16(292),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -147,7 +147,7 @@ bytecodes: [
/* 48 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 53 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 58 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(291),
B(Wide), B(LdaSmi), I16(293),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -179,7 +179,7 @@ bytecodes: [
/* 41 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 46 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 51 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(290),
B(Wide), B(LdaSmi), I16(292),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),

View File

@ -56,7 +56,7 @@ bytecodes: [
/* 44 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 49 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 54 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(289),
B(Wide), B(LdaSmi), I16(291),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -89,7 +89,7 @@ bytecodes: [
/* 44 E> */ B(StaKeyedPropertyAsDefine), R(this), R(0), U8(0),
/* 49 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 54 E> */ B(LdaKeyedProperty), R(this), U8(2),
B(Wide), B(LdaSmi), I16(289),
B(Wide), B(LdaSmi), I16(291),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),

View File

@ -24,7 +24,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(1),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -59,13 +59,13 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star1),
B(LdaConstant), U8(0),
B(Star2),
/* 61 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(Throw),
B(Wide), B(LdaSmi), I16(289),
B(Wide), B(LdaSmi), I16(291),
B(Star1),
B(LdaConstant), U8(1),
B(Star2),
@ -97,13 +97,13 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star1),
B(LdaConstant), U8(0),
B(Star2),
/* 61 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(Throw),
B(Wide), B(LdaSmi), I16(289),
B(Wide), B(LdaSmi), I16(291),
B(Star1),
B(LdaConstant), U8(1),
B(Star2),
@ -143,7 +143,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -165,7 +165,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star3),
B(LdaConstant), U8(0),
B(Star4),
@ -180,7 +180,7 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star2),
B(LdaConstant), U8(0),
B(Star3),
@ -214,13 +214,13 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star1),
B(LdaConstant), U8(0),
B(Star2),
/* 65 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(Throw),
B(Wide), B(LdaSmi), I16(291),
B(Wide), B(LdaSmi), I16(293),
B(Star1),
B(LdaConstant), U8(1),
B(Star2),
@ -251,13 +251,13 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star1),
B(LdaConstant), U8(0),
B(Star2),
/* 58 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(Throw),
B(Wide), B(LdaSmi), I16(290),
B(Wide), B(LdaSmi), I16(292),
B(Star1),
B(LdaConstant), U8(1),
B(Star2),
@ -288,13 +288,13 @@ bytecodes: [
B(TestReferenceEqual), R(this),
B(Mov), R(this), R(0),
B(JumpIfTrue), U8(16),
B(Wide), B(LdaSmi), I16(283),
B(Wide), B(LdaSmi), I16(285),
B(Star1),
B(LdaConstant), U8(0),
B(Star2),
/* 65 E> */ B(CallRuntime), U16(Runtime::kNewTypeError), R(1), U8(2),
B(Throw),
B(Wide), B(LdaSmi), I16(291),
B(Wide), B(LdaSmi), I16(293),
B(Star1),
B(LdaConstant), U8(1),
B(Star2),
@ -323,7 +323,7 @@ bytecode array length: 19
bytecodes: [
/* 46 S> */ B(LdaImmutableCurrentContextSlot), U8(3),
/* 51 E> */ B(LdaKeyedProperty), R(this), U8(0),
B(Wide), B(LdaSmi), I16(290),
B(Wide), B(LdaSmi), I16(292),
B(Star1),
B(LdaConstant), U8(0),
B(Star2),

View File

@ -0,0 +1,52 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct
"use strict";
let S = new SharedStructType(['field']);
(function TestNoPrototype() {
// For now the experimental shared structs don't have a prototype, unlike the
// proposal explainer which says accessing the prototype throws.
assertNull(S.prototype);
assertNull(Object.getPrototypeOf(new S()));
})();
(function TestPrimitives() {
// All primitives can be stored in fields.
let s = new S();
for (let prim of [42, -0, undefined, null, true, false, "foo"]) {
s.field = prim;
assertEquals(s.field, prim);
}
})();
(function TestObjects() {
let s = new S();
// Shared objects cannot point to non-shared objects.
assertThrows(() => { s.field = []; });
assertThrows(() => { s.field = {}; });
// Shared objects can point to other shared objects.
let shared_rhs = new S();
s.field = shared_rhs;
assertEquals(s.field, shared_rhs);
})();
(function TestNotExtensible() {
let s = new S();
// Shared structs are non-extensible.
assertThrows(() => { s.nonExistent = 42; });
assertThrows(() => { Object.setPrototypeOf(s, {}); });
assertThrows(() => { Object.defineProperty(s, 'nonExistent', { value: 42 }); });
})();
(function TestTooManyFields() {
let field_names = [];
for (let i = 0; i < 1000; i++) {
field_names.push('field' + i);
}
assertThrows(() => { new SharedStructType(field_names); });
})();

View File

@ -0,0 +1,39 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --shared-string-table --harmony-struct --allow-natives-syntax
"use strict";
if (this.Worker) {
(function TestSharedStructPostMessage() {
let workerScript =
`onmessage = function(struct) {
struct.struct_field.payload = 42;
struct.string_field = "worker";
postMessage("done");
};
postMessage("started");`;
let worker = new Worker(workerScript, { type: 'string' });
let started = worker.getMessage();
assertEquals("started", started);
let OuterStruct = new SharedStructType(['string_field', 'struct_field']);
let InnerStruct = new SharedStructType(['payload']);
let struct = new OuterStruct();
struct.struct_field = new InnerStruct();
struct.string_field = "main";
assertEquals("main", struct.string_field);
assertEquals(undefined, struct.struct_field.payload);
worker.postMessage(struct);
assertEquals("done", worker.getMessage());
assertEquals("worker", struct.string_field);
assertEquals(42, struct.struct_field.payload);
worker.terminate();
})();
}

View File

@ -244,27 +244,28 @@ INSTANCE_TYPES = {
2122: "JS_SEGMENTER_TYPE",
2123: "JS_SEGMENTS_TYPE",
2124: "JS_SHADOW_REALM_TYPE",
2125: "JS_STRING_ITERATOR_TYPE",
2126: "JS_TEMPORAL_CALENDAR_TYPE",
2127: "JS_TEMPORAL_DURATION_TYPE",
2128: "JS_TEMPORAL_INSTANT_TYPE",
2129: "JS_TEMPORAL_PLAIN_DATE_TYPE",
2130: "JS_TEMPORAL_PLAIN_DATE_TIME_TYPE",
2131: "JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE",
2132: "JS_TEMPORAL_PLAIN_TIME_TYPE",
2133: "JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE",
2134: "JS_TEMPORAL_TIME_ZONE_TYPE",
2135: "JS_TEMPORAL_ZONED_DATE_TIME_TYPE",
2136: "JS_V8_BREAK_ITERATOR_TYPE",
2137: "JS_WEAK_REF_TYPE",
2138: "WASM_GLOBAL_OBJECT_TYPE",
2139: "WASM_INSTANCE_OBJECT_TYPE",
2140: "WASM_MEMORY_OBJECT_TYPE",
2141: "WASM_MODULE_OBJECT_TYPE",
2142: "WASM_SUSPENDER_OBJECT_TYPE",
2143: "WASM_TABLE_OBJECT_TYPE",
2144: "WASM_TAG_OBJECT_TYPE",
2145: "WASM_VALUE_OBJECT_TYPE",
2125: "JS_SHARED_STRUCT_TYPE",
2126: "JS_STRING_ITERATOR_TYPE",
2127: "JS_TEMPORAL_CALENDAR_TYPE",
2128: "JS_TEMPORAL_DURATION_TYPE",
2129: "JS_TEMPORAL_INSTANT_TYPE",
2130: "JS_TEMPORAL_PLAIN_DATE_TYPE",
2131: "JS_TEMPORAL_PLAIN_DATE_TIME_TYPE",
2132: "JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE",
2133: "JS_TEMPORAL_PLAIN_TIME_TYPE",
2134: "JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE",
2135: "JS_TEMPORAL_TIME_ZONE_TYPE",
2136: "JS_TEMPORAL_ZONED_DATE_TIME_TYPE",
2137: "JS_V8_BREAK_ITERATOR_TYPE",
2138: "JS_WEAK_REF_TYPE",
2139: "WASM_GLOBAL_OBJECT_TYPE",
2140: "WASM_INSTANCE_OBJECT_TYPE",
2141: "WASM_MEMORY_OBJECT_TYPE",
2142: "WASM_MODULE_OBJECT_TYPE",
2143: "WASM_SUSPENDER_OBJECT_TYPE",
2144: "WASM_TABLE_OBJECT_TYPE",
2145: "WASM_TAG_OBJECT_TYPE",
2146: "WASM_VALUE_OBJECT_TYPE",
}
# List of known V8 maps.