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:
parent
ddc409cbd4
commit
efdf87aff8
@ -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",
|
||||
|
4
BUILD.gn
4
BUILD.gn
@ -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",
|
||||
|
@ -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'
|
||||
|
@ -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()));
|
||||
|
@ -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) \
|
||||
|
123
src/builtins/builtins-struct.cc
Normal file
123
src/builtins/builtins-struct.cc
Normal 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
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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") \
|
||||
|
@ -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, \
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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>();
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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) |
|
||||
|
@ -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);
|
||||
|
@ -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())) {
|
||||
|
@ -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(¬_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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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());
|
||||
|
@ -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:
|
||||
|
30
src/objects/js-struct-inl.h
Normal file
30
src/objects/js-struct-inl.h
Normal 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
35
src/objects/js-struct.h
Normal 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
7
src/objects/js-struct.tq
Normal 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;
|
||||
}
|
@ -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)) {
|
||||
|
@ -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:
|
||||
|
@ -159,6 +159,7 @@ class ZoneForwardList;
|
||||
V(JSSet) \
|
||||
V(JSSetIterator) \
|
||||
V(JSShadowRealm) \
|
||||
V(JSSharedStruct) \
|
||||
V(JSSpecialObject) \
|
||||
V(JSStringIterator) \
|
||||
V(JSTemporalCalendar) \
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
||||
##############################################################################
|
||||
|
52
test/mjsunit/shared-memory/shared-struct-surface.js
Normal file
52
test/mjsunit/shared-memory/shared-struct-surface.js
Normal 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); });
|
||||
})();
|
39
test/mjsunit/shared-memory/shared-struct-workers.js
Normal file
39
test/mjsunit/shared-memory/shared-struct-workers.js
Normal 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();
|
||||
})();
|
||||
|
||||
}
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user