Reland "[shared-struct] Prototype JS shared structs"

This is a reland of 1025bf26e3

Changes since revert:

- TSAN issue fixed by https://crrev.com/c/3475084
- Skip the shared-struct-workers test until shared GC deadlock is fixed,
  being tracked in v8:12645

Original change's description:
> [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}

Bug: v8:12547
Change-Id: Ic1f5cf9fa9791ae2d5d5dc7c110614ca10b5d98e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3475078
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79215}
This commit is contained in:
Shu-yu Guo 2022-02-22 16:36:17 -08:00 committed by V8 LUCI CQ
parent ddc409cbd4
commit efdf87aff8
55 changed files with 877 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

@ -1775,6 +1775,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",
@ -3186,6 +3187,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",
@ -4060,6 +4063,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

@ -5191,7 +5191,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) \
@ -719,6 +720,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_DARWIN) || !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);
@ -1846,15 +1847,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));
@ -1865,7 +1870,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);
@ -1874,20 +1880,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

@ -6382,7 +6382,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

@ -331,7 +331,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

@ -1161,6 +1161,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());
@ -2147,8 +2155,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

@ -738,5 +738,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

@ -235,6 +235,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

@ -273,6 +273,9 @@
# TODO(v8:10915): Fails with --future.
'harmony/weakrefs/stress-finalizationregistry-dirty-enqueue': [SKIP],
# BUG(v8:12645)
'shared-memory/shared-struct-workers': [SKIP],
}], # ALWAYS
##############################################################################

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.