From 1ce901b164c6436f6e1747227384a832d7d5c5bf Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 1 Dec 2023 18:13:50 +0100 Subject: [PATCH] deps: V8: cherry-pick c400af48b5ef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: [symbol-as-weakmap-key] Implement Symbol as WeakMap Keys Allow non-registered symbols as keys in weakmap and weakset. Allow non-registered symbols as target and unregisterToken in WeakRef and FinalizationRegistry. Bug: v8:12947 Change-Id: Ieb63bda66e3cc378879ac651e23300b71caed627 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3865056 Reviewed-by: Dominik Inführ Commit-Queue: Marja Hölttä Reviewed-by: Jakob Linke Cr-Commit-Position: refs/heads/main@{#83313} Refs: https://github.com/v8/v8/commit/c400af48b5ef3c5c54312fec8acd0d84e6071046 PR-URL: https://github.com/nodejs/node/pull/51004 Reviewed-By: Chengzhong Wu --- common.gypi | 2 +- deps/v8/src/builtins/base.tq | 10 +- .../src/builtins/builtins-collections-gen.cc | 230 +++--------------- .../src/builtins/builtins-collections-gen.h | 186 ++++++++++++++ deps/v8/src/builtins/builtins-weak-refs.cc | 28 ++- deps/v8/src/builtins/cast.tq | 15 ++ deps/v8/src/builtins/finalization-registry.tq | 38 +-- deps/v8/src/builtins/weak-ref.tq | 20 +- deps/v8/src/codegen/code-stub-assembler.h | 6 + deps/v8/src/codegen/external-reference.cc | 5 + deps/v8/src/codegen/external-reference.h | 2 + deps/v8/src/common/message-template.h | 10 +- deps/v8/src/diagnostics/objects-debug.cc | 8 +- deps/v8/src/flags/flag-definitions.h | 3 +- deps/v8/src/heap/heap.cc | 2 +- deps/v8/src/heap/heap.h | 2 +- deps/v8/src/init/bootstrapper.cc | 1 + deps/v8/src/objects/js-weak-refs-inl.h | 11 +- deps/v8/src/objects/js-weak-refs.h | 4 +- deps/v8/src/objects/js-weak-refs.tq | 9 +- deps/v8/src/runtime/runtime-weak-refs.cc | 12 +- .../test/message/fail/weak-refs-register1.out | 4 +- .../message/fail/weak-refs-unregister.out | 4 +- .../mjsunit/harmony/symbol-as-weakmap-key.js | 99 ++++++++ .../test/mjsunit/harmony/weakrefs/basics.js | 4 +- .../weakrefs/symbol-as-weakref-target-gc.js | 39 +++ .../weakrefs/symbol-as-weakref-target.js | 41 ++++ 27 files changed, 534 insertions(+), 261 deletions(-) create mode 100644 deps/v8/test/mjsunit/harmony/symbol-as-weakmap-key.js create mode 100644 deps/v8/test/mjsunit/harmony/weakrefs/symbol-as-weakref-target-gc.js create mode 100644 deps/v8/test/mjsunit/harmony/weakrefs/symbol-as-weakref-target.js diff --git a/common.gypi b/common.gypi index dba2631f218581..a58d48b0c5eb9a 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.28', + 'v8_embedder_string': '-node.29', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/builtins/base.tq b/deps/v8/src/builtins/base.tq index c34d9f6884cfb5..fe67d403b87879 100644 --- a/deps/v8/src/builtins/base.tq +++ b/deps/v8/src/builtins/base.tq @@ -438,9 +438,9 @@ extern enum MessageTemplate { kWasmTrapArrayOutOfBounds, kWasmTrapArrayTooLarge, kWeakRefsRegisterTargetAndHoldingsMustNotBeSame, - kWeakRefsRegisterTargetMustBeObject, - kWeakRefsUnregisterTokenMustBeObject, - kWeakRefsWeakRefConstructorTargetMustBeObject, + kInvalidWeakRefsRegisterTarget, + kInvalidWeakRefsUnregisterToken, + kInvalidWeakRefsWeakRefConstructorTarget, ... } @@ -906,10 +906,10 @@ macro Float64IsNaN(n: float64): bool { // The type of all tagged values that can safely be compared with TaggedEqual. @if(V8_ENABLE_WEBASSEMBLY) type TaggedWithIdentity = JSReceiver | FixedArrayBase | Oddball | Map | - WeakCell | Context | EmptyString | WasmInternalFunction; + WeakCell | Context | EmptyString | Symbol | WasmInternalFunction; @ifnot(V8_ENABLE_WEBASSEMBLY) type TaggedWithIdentity = JSReceiver | FixedArrayBase | Oddball | Map | - WeakCell | Context | EmptyString; + WeakCell | Context | EmptyString | Symbol; extern operator '==' macro TaggedEqual(TaggedWithIdentity, Object): bool; extern operator '==' macro TaggedEqual(Object, TaggedWithIdentity): bool; diff --git a/deps/v8/src/builtins/builtins-collections-gen.cc b/deps/v8/src/builtins/builtins-collections-gen.cc index 00cad7b314b756..313b66b176f7f5 100644 --- a/deps/v8/src/builtins/builtins-collections-gen.cc +++ b/deps/v8/src/builtins/builtins-collections-gen.cc @@ -22,130 +22,6 @@ namespace internal { template using TVariable = compiler::TypedCodeAssemblerVariable; -class BaseCollectionsAssembler : public CodeStubAssembler { - public: - explicit BaseCollectionsAssembler(compiler::CodeAssemblerState* state) - : CodeStubAssembler(state) {} - - virtual ~BaseCollectionsAssembler() = default; - - protected: - enum Variant { kMap, kSet, kWeakMap, kWeakSet }; - - // Adds an entry to a collection. For Maps, properly handles extracting the - // key and value from the entry (see LoadKeyValue()). - void AddConstructorEntry(Variant variant, TNode context, - TNode collection, TNode add_function, - TNode key_value, - Label* if_may_have_side_effects = nullptr, - Label* if_exception = nullptr, - TVariable* var_exception = nullptr); - - // Adds constructor entries to a collection. Choosing a fast path when - // possible. - void AddConstructorEntries(Variant variant, TNode context, - TNode native_context, - TNode collection, - TNode initial_entries); - - // Fast path for adding constructor entries. Assumes the entries are a fast - // JS array (see CodeStubAssembler::BranchIfFastJSArray()). - void AddConstructorEntriesFromFastJSArray(Variant variant, - TNode context, - TNode native_context, - TNode collection, - TNode fast_jsarray, - Label* if_may_have_side_effects); - - // Adds constructor entries to a collection using the iterator protocol. - void AddConstructorEntriesFromIterable(Variant variant, - TNode context, - TNode native_context, - TNode collection, - TNode iterable); - - // Constructs a collection instance. Choosing a fast path when possible. - TNode AllocateJSCollection(TNode context, - TNode constructor, - TNode new_target); - - // Fast path for constructing a collection instance if the constructor - // function has not been modified. - TNode AllocateJSCollectionFast(TNode constructor); - - // Fallback for constructing a collection instance if the constructor function - // has been modified. - TNode AllocateJSCollectionSlow(TNode context, - TNode constructor, - TNode new_target); - - // Allocates the backing store for a collection. - virtual TNode AllocateTable( - Variant variant, TNode at_least_space_for) = 0; - - // Main entry point for a collection constructor builtin. - void GenerateConstructor(Variant variant, - Handle constructor_function_name, - TNode new_target, TNode argc, - TNode context); - - // Retrieves the collection function that adds an entry. `set` for Maps and - // `add` for Sets. - TNode GetAddFunction(Variant variant, TNode context, - TNode collection); - - // Retrieves the collection constructor function. - TNode GetConstructor(Variant variant, - TNode native_context); - - // Retrieves the initial collection function that adds an entry. Should only - // be called when it is certain that a collection prototype's map hasn't been - // changed. - TNode GetInitialAddFunction(Variant variant, - TNode native_context); - - // Checks whether {collection}'s initial add/set function has been modified - // (depending on {variant}, loaded from {native_context}). - void GotoIfInitialAddFunctionModified(Variant variant, - TNode native_context, - TNode collection, - Label* if_modified); - - // Gets root index for the name of the add/set function. - RootIndex GetAddFunctionNameIndex(Variant variant); - - // Retrieves the offset to access the backing table from the collection. - int GetTableOffset(Variant variant); - - // Estimates the number of entries the collection will have after adding the - // entries passed in the constructor. AllocateTable() can use this to avoid - // the time of growing/rehashing when adding the constructor entries. - TNode EstimatedInitialSize(TNode initial_entries, - TNode is_fast_jsarray); - - void GotoIfCannotBeWeakKey(const TNode obj, - Label* if_cannot_be_weak_key); - - // Determines whether the collection's prototype has been modified. - TNode HasInitialCollectionPrototype(Variant variant, - TNode native_context, - TNode collection); - - // Gets the initial prototype map for given collection {variant}. - TNode GetInitialCollectionPrototype(Variant variant, - TNode native_context); - - // Loads an element from a fixed array. If the element is the hole, returns - // `undefined`. - TNode LoadAndNormalizeFixedArrayElement(TNode elements, - TNode index); - - // Loads an element from a fixed double array. If the element is the hole, - // returns `undefined`. - TNode LoadAndNormalizeFixedDoubleArrayElement( - TNode elements, TNode index); -}; - void BaseCollectionsAssembler::AddConstructorEntry( Variant variant, TNode context, TNode collection, TNode add_function, TNode key_value, @@ -525,12 +401,23 @@ TNode BaseCollectionsAssembler::EstimatedInitialSize( void BaseCollectionsAssembler::GotoIfCannotBeWeakKey( const TNode obj, Label* if_cannot_be_weak_key) { + Label check_symbol_key(this); + Label end(this); GotoIf(TaggedIsSmi(obj), if_cannot_be_weak_key); TNode instance_type = LoadMapInstanceType(LoadMap(CAST(obj))); - GotoIfNot(IsJSReceiverInstanceType(instance_type), if_cannot_be_weak_key); + GotoIfNot(IsJSReceiverInstanceType(instance_type), &check_symbol_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); + Goto(&end); + Bind(&check_symbol_key); + GotoIfNot(HasHarmonySymbolAsWeakmapKeyFlag(), if_cannot_be_weak_key); + GotoIfNot(IsSymbolInstanceType(instance_type), if_cannot_be_weak_key); + TNode flags = LoadSymbolFlags(CAST(obj)); + GotoIf(Word32And(flags, Symbol::IsInPublicSymbolTableBit::kMask), + if_cannot_be_weak_key); + Goto(&end); + Bind(&end); } TNode BaseCollectionsAssembler::GetInitialCollectionPrototype( @@ -2427,67 +2314,6 @@ TF_BUILTIN(FindOrderedHashMapEntry, CollectionsBuiltinsAssembler) { Return(SmiConstant(-1)); } -class WeakCollectionsBuiltinsAssembler : public BaseCollectionsAssembler { - public: - explicit WeakCollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state) - : BaseCollectionsAssembler(state) {} - - protected: - void AddEntry(TNode table, TNode key_index, - TNode key, TNode value, - TNode number_of_elements); - - TNode AllocateTable(Variant variant, - TNode at_least_space_for) override; - - // Generates and sets the identity for a JSRececiver. - TNode CreateIdentityHash(TNode receiver); - TNode EntryMask(TNode capacity); - - // Builds code that finds the EphemeronHashTable entry for a {key} using the - // comparison code generated by {key_compare}. The key index is returned if - // the {key} is found. - using KeyComparator = - std::function entry_key, Label* if_same)>; - TNode FindKeyIndex(TNode table, TNode key_hash, - TNode entry_mask, - const KeyComparator& key_compare); - - // Builds code that finds an EphemeronHashTable entry available for a new - // entry. - TNode FindKeyIndexForInsertion(TNode table, - TNode key_hash, - TNode entry_mask); - - // Builds code that finds the EphemeronHashTable entry with key that matches - // {key} and returns the entry's key index. If {key} cannot be found, jumps to - // {if_not_found}. - TNode FindKeyIndexForKey(TNode table, TNode key, - TNode hash, - TNode entry_mask, - Label* if_not_found); - - TNode InsufficientCapacityToAdd(TNode capacity, - TNode number_of_elements, - TNode number_of_deleted); - TNode KeyIndexFromEntry(TNode entry); - - TNode LoadNumberOfElements(TNode table, - int offset); - TNode LoadNumberOfDeleted(TNode table, - int offset = 0); - TNode LoadTable(TNode collection); - TNode LoadTableCapacity(TNode table); - - void RemoveEntry(TNode table, TNode key_index, - TNode number_of_elements); - TNode ShouldRehash(TNode number_of_elements, - TNode number_of_deleted); - TNode ShouldShrink(TNode capacity, - TNode number_of_elements); - TNode ValueIndexFromKeyIndex(TNode key_index); -}; - void WeakCollectionsBuiltinsAssembler::AddEntry( TNode table, TNode key_index, TNode key, TNode value, TNode number_of_elements) { @@ -2503,6 +2329,25 @@ void WeakCollectionsBuiltinsAssembler::AddEntry( SmiFromIntPtr(number_of_elements)); } +TNode WeakCollectionsBuiltinsAssembler::GetHash( + const TNode key, Label* if_no_hash) { + TVARIABLE(IntPtrT, var_hash); + Label if_symbol(this); + Label return_result(this); + GotoIfNot(IsJSReceiver(key), &if_symbol); + var_hash = LoadJSReceiverIdentityHash(CAST(key), if_no_hash); + Goto(&return_result); + Bind(&if_symbol); + CSA_DCHECK(this, IsSymbol(key)); + CSA_DCHECK(this, Word32BinaryNot( + Word32And(LoadSymbolFlags(CAST(key)), + Symbol::IsInPublicSymbolTableBit::kMask))); + var_hash = ChangeInt32ToIntPtr(LoadNameHash(CAST(key), nullptr)); + Goto(&return_result); + Bind(&return_result); + return var_hash.value(); +} + TNode WeakCollectionsBuiltinsAssembler::AllocateTable( Variant variant, TNode at_least_space_for) { // See HashTable::New(). @@ -2732,8 +2577,7 @@ TF_BUILTIN(WeakMapLookupHashIndex, WeakCollectionsBuiltinsAssembler) { GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key); - TNode hash = - LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key); + TNode hash = GetHash(CAST(key), &if_cannot_be_weak_key); TNode capacity = LoadTableCapacity(table); TNode key_index = FindKeyIndexForKey( table, key, hash, EntryMask(capacity), &if_cannot_be_weak_key); @@ -2798,8 +2642,7 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) { GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key); - TNode hash = - LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key); + TNode hash = GetHash(CAST(key), &if_cannot_be_weak_key); TNode table = LoadTable(collection); TNode capacity = LoadTableCapacity(table); TNode key_index = FindKeyIndexForKey( @@ -2823,10 +2666,10 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) { TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) { auto context = Parameter(Descriptor::kContext); auto collection = Parameter(Descriptor::kCollection); - auto key = Parameter(Descriptor::kKey); + auto key = Parameter(Descriptor::kKey); auto value = Parameter(Descriptor::kValue); - CSA_DCHECK(this, IsJSReceiver(key)); + CSA_DCHECK(this, Word32Or(IsJSReceiver(key), IsSymbol(key))); Label call_runtime(this), if_no_hash(this), if_not_found(this); @@ -2834,7 +2677,7 @@ TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) { TNode capacity = LoadTableCapacity(table); TNode entry_mask = EntryMask(capacity); - TVARIABLE(IntPtrT, var_hash, LoadJSReceiverIdentityHash(key, &if_no_hash)); + TVARIABLE(IntPtrT, var_hash, GetHash(key, &if_no_hash)); TNode key_index = FindKeyIndexForKey(table, key, var_hash.value(), entry_mask, &if_not_found); @@ -2843,6 +2686,7 @@ TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) { BIND(&if_no_hash); { + CSA_DCHECK(this, IsJSReceiver(key)); var_hash = SmiUntag(CreateIdentityHash(key)); Goto(&if_not_found); } diff --git a/deps/v8/src/builtins/builtins-collections-gen.h b/deps/v8/src/builtins/builtins-collections-gen.h index a132557e3cd0a4..6b5516a818b332 100644 --- a/deps/v8/src/builtins/builtins-collections-gen.h +++ b/deps/v8/src/builtins/builtins-collections-gen.h @@ -20,6 +20,192 @@ void BranchIfIterableWithOriginalValueSetIterator( TNode context, compiler::CodeAssemblerLabel* if_true, compiler::CodeAssemblerLabel* if_false); +class BaseCollectionsAssembler : public CodeStubAssembler { + public: + explicit BaseCollectionsAssembler(compiler::CodeAssemblerState* state) + : CodeStubAssembler(state) {} + + virtual ~BaseCollectionsAssembler() = default; + + void GotoIfCannotBeWeakKey(const TNode obj, + Label* if_cannot_be_weak_key); + + protected: + enum Variant { kMap, kSet, kWeakMap, kWeakSet }; + + // Adds an entry to a collection. For Maps, properly handles extracting the + // key and value from the entry (see LoadKeyValue()). + void AddConstructorEntry(Variant variant, TNode context, + TNode collection, TNode add_function, + TNode key_value, + Label* if_may_have_side_effects = nullptr, + Label* if_exception = nullptr, + TVariable* var_exception = nullptr); + + // Adds constructor entries to a collection. Choosing a fast path when + // possible. + void AddConstructorEntries(Variant variant, TNode context, + TNode native_context, + TNode collection, + TNode initial_entries); + + // Fast path for adding constructor entries. Assumes the entries are a fast + // JS array (see CodeStubAssembler::BranchIfFastJSArray()). + void AddConstructorEntriesFromFastJSArray(Variant variant, + TNode context, + TNode native_context, + TNode collection, + TNode fast_jsarray, + Label* if_may_have_side_effects); + + // Adds constructor entries to a collection using the iterator protocol. + void AddConstructorEntriesFromIterable(Variant variant, + TNode context, + TNode native_context, + TNode collection, + TNode iterable); + + // Constructs a collection instance. Choosing a fast path when possible. + TNode AllocateJSCollection(TNode context, + TNode constructor, + TNode new_target); + + // Fast path for constructing a collection instance if the constructor + // function has not been modified. + TNode AllocateJSCollectionFast(TNode constructor); + + // Fallback for constructing a collection instance if the constructor function + // has been modified. + TNode AllocateJSCollectionSlow(TNode context, + TNode constructor, + TNode new_target); + + // Allocates the backing store for a collection. + virtual TNode AllocateTable( + Variant variant, TNode at_least_space_for) = 0; + + // Main entry point for a collection constructor builtin. + void GenerateConstructor(Variant variant, + Handle constructor_function_name, + TNode new_target, TNode argc, + TNode context); + + // Retrieves the collection function that adds an entry. `set` for Maps and + // `add` for Sets. + TNode GetAddFunction(Variant variant, TNode context, + TNode collection); + + // Retrieves the collection constructor function. + TNode GetConstructor(Variant variant, + TNode native_context); + + // Retrieves the initial collection function that adds an entry. Should only + // be called when it is certain that a collection prototype's map hasn't been + // changed. + TNode GetInitialAddFunction(Variant variant, + TNode native_context); + + // Checks whether {collection}'s initial add/set function has been modified + // (depending on {variant}, loaded from {native_context}). + void GotoIfInitialAddFunctionModified(Variant variant, + TNode native_context, + TNode collection, + Label* if_modified); + + // Gets root index for the name of the add/set function. + RootIndex GetAddFunctionNameIndex(Variant variant); + + // Retrieves the offset to access the backing table from the collection. + int GetTableOffset(Variant variant); + + // Estimates the number of entries the collection will have after adding the + // entries passed in the constructor. AllocateTable() can use this to avoid + // the time of growing/rehashing when adding the constructor entries. + TNode EstimatedInitialSize(TNode initial_entries, + TNode is_fast_jsarray); + + // Determines whether the collection's prototype has been modified. + TNode HasInitialCollectionPrototype(Variant variant, + TNode native_context, + TNode collection); + + // Gets the initial prototype map for given collection {variant}. + TNode GetInitialCollectionPrototype(Variant variant, + TNode native_context); + + // Loads an element from a fixed array. If the element is the hole, returns + // `undefined`. + TNode LoadAndNormalizeFixedArrayElement(TNode elements, + TNode index); + + // Loads an element from a fixed double array. If the element is the hole, + // returns `undefined`. + TNode LoadAndNormalizeFixedDoubleArrayElement( + TNode elements, TNode index); +}; + +class WeakCollectionsBuiltinsAssembler : public BaseCollectionsAssembler { + public: + explicit WeakCollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state) + : BaseCollectionsAssembler(state) {} + + protected: + void AddEntry(TNode table, TNode key_index, + TNode key, TNode value, + TNode number_of_elements); + + TNode AllocateTable(Variant variant, + TNode at_least_space_for) override; + + TNode GetHash(const TNode key, Label* if_no_hash); + // Generates and sets the identity for a JSRececiver. + TNode CreateIdentityHash(TNode receiver); + TNode EntryMask(TNode capacity); + + // Builds code that finds the EphemeronHashTable entry for a {key} using the + // comparison code generated by {key_compare}. The key index is returned if + // the {key} is found. + using KeyComparator = + std::function entry_key, Label* if_same)>; + TNode FindKeyIndex(TNode table, TNode key_hash, + TNode entry_mask, + const KeyComparator& key_compare); + + // Builds code that finds an EphemeronHashTable entry available for a new + // entry. + TNode FindKeyIndexForInsertion(TNode table, + TNode key_hash, + TNode entry_mask); + + // Builds code that finds the EphemeronHashTable entry with key that matches + // {key} and returns the entry's key index. If {key} cannot be found, jumps to + // {if_not_found}. + TNode FindKeyIndexForKey(TNode table, TNode key, + TNode hash, + TNode entry_mask, + Label* if_not_found); + + TNode InsufficientCapacityToAdd(TNode capacity, + TNode number_of_elements, + TNode number_of_deleted); + TNode KeyIndexFromEntry(TNode entry); + + TNode LoadNumberOfElements(TNode table, + int offset); + TNode LoadNumberOfDeleted(TNode table, + int offset = 0); + TNode LoadTable(TNode collection); + TNode LoadTableCapacity(TNode table); + + void RemoveEntry(TNode table, TNode key_index, + TNode number_of_elements); + TNode ShouldRehash(TNode number_of_elements, + TNode number_of_deleted); + TNode ShouldShrink(TNode capacity, + TNode number_of_elements); + TNode ValueIndexFromKeyIndex(TNode key_index); +}; + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/builtins/builtins-weak-refs.cc b/deps/v8/src/builtins/builtins-weak-refs.cc index aee330b4bd4f3c..c76c3989d47507 100644 --- a/deps/v8/src/builtins/builtins-weak-refs.cc +++ b/deps/v8/src/builtins/builtins-weak-refs.cc @@ -9,6 +9,7 @@ namespace v8 { namespace internal { +// https://tc39.es/proposal-symbols-as-weakmap-keys/#sec-finalization-registry.prototype.unregister BUILTIN(FinalizationRegistryUnregister) { HandleScope scope(isolate); const char* method_name = "FinalizationRegistry.prototype.unregister"; @@ -24,16 +25,29 @@ BUILTIN(FinalizationRegistryUnregister) { Handle unregister_token = args.atOrUndefined(isolate, 1); - // 4. If Type(unregisterToken) is not Object, throw a TypeError exception. - if (!unregister_token->IsJSReceiver()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, - NewTypeError(MessageTemplate::kWeakRefsUnregisterTokenMustBeObject, - unregister_token)); + // 4. If CanBeHeldWeakly(unregisterToken) is false, throw a TypeError + // exception. + if (FLAG_harmony_symbol_as_weakmap_key) { + if (!unregister_token->IsJSReceiver() && + (!unregister_token->IsSymbol() || + Handle::cast(unregister_token)->is_in_public_symbol_table())) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, + NewTypeError(MessageTemplate::kInvalidWeakRefsUnregisterToken, + unregister_token)); + } + } else { + // 4. If Type(unregisterToken) is not Object, throw a TypeError exception. + if (!unregister_token->IsJSReceiver()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, + NewTypeError(MessageTemplate::kInvalidWeakRefsUnregisterToken, + unregister_token)); + } } bool success = JSFinalizationRegistry::Unregister( - finalization_registry, Handle::cast(unregister_token), + finalization_registry, Handle::cast(unregister_token), isolate); return *isolate->factory()->ToBoolean(success); diff --git a/deps/v8/src/builtins/cast.tq b/deps/v8/src/builtins/cast.tq index 5cb6e0bc92e0ed..0d347e3dd35cdc 100644 --- a/deps/v8/src/builtins/cast.tq +++ b/deps/v8/src/builtins/cast.tq @@ -697,6 +697,21 @@ Cast(o: HeapObject): JSReceiver|Null } } +Cast(implicit context: Context)(o: Object): JSReceiver|Symbol + labels CastError { + typeswitch (o) { + case (o: JSReceiver): { + return o; + } + case (o: Symbol): { + return o; + } + case (Object): { + goto CastError; + } + } +} + Cast(o: Object): Smi|PromiseReaction labels CastError { typeswitch (o) { case (o: Smi): { diff --git a/deps/v8/src/builtins/finalization-registry.tq b/deps/v8/src/builtins/finalization-registry.tq index 38cae7ed20b9ff..3dd554b382cf91 100644 --- a/deps/v8/src/builtins/finalization-registry.tq +++ b/deps/v8/src/builtins/finalization-registry.tq @@ -1,6 +1,7 @@ // Copyright 2020 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-collections-gen.h" namespace runtime { extern runtime @@ -15,6 +16,9 @@ extern transitioning macro RemoveFinalizationRegistryCellFromUnregisterTokenMap( JSFinalizationRegistry, WeakCell): void; +extern macro WeakCollectionsBuiltinsAssembler::GotoIfCannotBeWeakKey(JSAny): + void labels NotWeakKey; + macro SplitOffTail(weakCell: WeakCell): WeakCell|Undefined { const weakCellTail = weakCell.next; weakCell.next = Undefined; @@ -125,6 +129,7 @@ FinalizationRegistryConstructor( return finalizationRegistry; } +// https://tc39.es/proposal-symbols-as-weakmap-keys/#sec-finalization-registry.prototype.register transitioning javascript builtin FinalizationRegistryRegister( js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { @@ -134,33 +139,32 @@ FinalizationRegistryRegister( ThrowTypeError( MessageTemplate::kIncompatibleMethodReceiver, 'FinalizationRegistry.prototype.register', receiver); - // 3. If Type(target) is not Object, throw a TypeError exception. - const target = Cast(arguments[0]) otherwise ThrowTypeError( - MessageTemplate::kWeakRefsRegisterTargetMustBeObject); + // 3. If CanBeHeldWeakly(target) is false, throw a TypeError exception. + GotoIfCannotBeWeakKey(arguments[0]) + otherwise ThrowTypeError(MessageTemplate::kInvalidWeakRefsRegisterTarget); + + const target = UnsafeCast<(JSReceiver | Symbol)>(arguments[0]); const heldValue = arguments[1]; // 4. If SameValue(target, heldValue), throw a TypeError exception. if (target == heldValue) { ThrowTypeError( MessageTemplate::kWeakRefsRegisterTargetAndHoldingsMustNotBeSame); } - // 5. If Type(unregisterToken) is not Object, + // 5. If CanBeHeldWeakly(unregisterToken) is false, // a. If unregisterToken is not undefined, throw a TypeError exception. // b. Set unregisterToken to empty. const unregisterTokenRaw = arguments[2]; - let unregisterToken: JSReceiver|Undefined; - typeswitch (unregisterTokenRaw) { - case (Undefined): { - unregisterToken = Undefined; - } - case (unregisterTokenObj: JSReceiver): { - unregisterToken = unregisterTokenObj; - } - case (JSAny): deferred { - ThrowTypeError( - MessageTemplate::kWeakRefsUnregisterTokenMustBeObject, - unregisterTokenRaw); - } + let unregisterToken: JSReceiver|Undefined|Symbol; + + if (IsUndefined(unregisterTokenRaw)) { + unregisterToken = Undefined; + } else { + GotoIfCannotBeWeakKey(unregisterTokenRaw) + otherwise ThrowTypeError( + MessageTemplate::kInvalidWeakRefsUnregisterToken, unregisterTokenRaw); + unregisterToken = UnsafeCast<(JSReceiver | Symbol)>(unregisterTokenRaw); } + // 6. Let cell be the Record { [[WeakRefTarget]] : target, [[HeldValue]]: // heldValue, [[UnregisterToken]]: unregisterToken }. // Allocate the WeakCell object in the old space, because 1) WeakCell weakness diff --git a/deps/v8/src/builtins/weak-ref.tq b/deps/v8/src/builtins/weak-ref.tq index 56d3fc1c4314bf..68f56fc111ad36 100644 --- a/deps/v8/src/builtins/weak-ref.tq +++ b/deps/v8/src/builtins/weak-ref.tq @@ -2,15 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include 'src/builtins/builtins-collections-gen.h' + namespace runtime { -extern runtime JSWeakRefAddToKeptObjects(implicit context: Context)(JSReceiver): - void; +extern runtime JSWeakRefAddToKeptObjects(implicit context: Context)( + JSReceiver | Symbol): void; } // namespace runtime namespace weakref { +// https://tc39.es/proposal-symbols-as-weakmap-keys/#sec-weak-ref-target transitioning javascript builtin WeakRefConstructor( js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny, @@ -19,15 +22,17 @@ WeakRefConstructor( if (newTarget == Undefined) { ThrowTypeError(MessageTemplate::kConstructorNotFunction, 'WeakRef'); } - // 2. If Type(target) is not Object, throw a TypeError exception. - const weakTarget = Cast(weakTarget) otherwise - ThrowTypeError( - MessageTemplate::kWeakRefsWeakRefConstructorTargetMustBeObject); + + // 2. If CanBeHeldWeakly(weakTarget) is false, throw a TypeError exception. + GotoIfCannotBeWeakKey(weakTarget) otherwise ThrowTypeError( + MessageTemplate::kInvalidWeakRefsWeakRefConstructorTarget); + // 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget, // "%WeakRefPrototype%", « [[WeakRefTarget]] »). const map = GetDerivedMap(target, UnsafeCast(newTarget)); const weakRef = UnsafeCast(AllocateFastOrSlowJSObjectFromMap(map)); // 4. Perfom ! AddToKeptObjects(target). + const weakTarget = UnsafeCast<(JSReceiver | Symbol)>(weakTarget); runtime::JSWeakRefAddToKeptObjects(weakTarget); // 5. Set weakRef.[[WeakRefTarget]] to target. weakRef.target = weakTarget; @@ -52,7 +57,8 @@ WeakRefDeref(js-implicit context: NativeContext, receiver: JSAny)(): JSAny { if (target != Undefined) { // JSWeakRefAddToKeptObjects might allocate and cause a GC, but it // won't clear `target` since we hold it here on the stack. - runtime::JSWeakRefAddToKeptObjects(UnsafeCast(target)); + runtime::JSWeakRefAddToKeptObjects( + UnsafeCast<(JSReceiver | Symbol)>(target)); } return target; } diff --git a/deps/v8/src/codegen/code-stub-assembler.h b/deps/v8/src/codegen/code-stub-assembler.h index bccdc34b746bdb..a1c8816c54b90b 100644 --- a/deps/v8/src/codegen/code-stub-assembler.h +++ b/deps/v8/src/codegen/code-stub-assembler.h @@ -2679,6 +2679,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ExternalReference::address_of_shared_string_table_flag()); } + TNode HasHarmonySymbolAsWeakmapKeyFlag() { + return LoadRuntimeFlag( + ExternalReference:: + address_of_FLAG_harmony_symbol_as_weakmap_key()); + } + // True iff |object| is a Smi or a HeapNumber or a BigInt. TNode IsNumeric(TNode object); diff --git a/deps/v8/src/codegen/external-reference.cc b/deps/v8/src/codegen/external-reference.cc index 6d206663ba86b7..686a91da6dc4b5 100644 --- a/deps/v8/src/codegen/external-reference.cc +++ b/deps/v8/src/codegen/external-reference.cc @@ -550,6 +550,11 @@ ExternalReference::address_of_mock_arraybuffer_allocator_flag() { return ExternalReference(&FLAG_mock_arraybuffer_allocator); } +ExternalReference +ExternalReference::address_of_FLAG_harmony_symbol_as_weakmap_key() { + return ExternalReference(&FLAG_harmony_symbol_as_weakmap_key); +} + ExternalReference ExternalReference::address_of_builtin_subclassing_flag() { return ExternalReference(&FLAG_builtin_subclassing); } diff --git a/deps/v8/src/codegen/external-reference.h b/deps/v8/src/codegen/external-reference.h index 7715aa6a816f33..636c528dabfe41 100644 --- a/deps/v8/src/codegen/external-reference.h +++ b/deps/v8/src/codegen/external-reference.h @@ -92,6 +92,8 @@ class StatsCounter; #define EXTERNAL_REFERENCE_LIST(V) \ V(abort_with_reason, "abort_with_reason") \ + V(address_of_FLAG_harmony_symbol_as_weakmap_key, \ + "FLAG_harmony_symbol_as_weakmap_key") \ V(address_of_builtin_subclassing_flag, "FLAG_builtin_subclassing") \ V(address_of_double_abs_constant, "double_absolute_constant") \ V(address_of_double_neg_constant, "double_negate_constant") \ diff --git a/deps/v8/src/common/message-template.h b/deps/v8/src/common/message-template.h index 22fde49ffd1673..a37d278d6e17c9 100644 --- a/deps/v8/src/common/message-template.h +++ b/deps/v8/src/common/message-template.h @@ -633,17 +633,15 @@ namespace internal { T(TraceEventPhaseError, "Trace event phase must be a number.") \ T(TraceEventIDError, "Trace event id must be a number.") \ /* Weak refs */ \ - T(WeakRefsUnregisterTokenMustBeObject, \ - "unregisterToken ('%') must be an object") \ + T(InvalidWeakRefsUnregisterToken, "Invalid unregisterToken ('%')") \ T(WeakRefsCleanupMustBeCallable, \ "FinalizationRegistry: cleanup must be callable") \ - T(WeakRefsRegisterTargetMustBeObject, \ - "FinalizationRegistry.prototype.register: target must be an object") \ + T(InvalidWeakRefsRegisterTarget, \ + "FinalizationRegistry.prototype.register: invalid target") \ T(WeakRefsRegisterTargetAndHoldingsMustNotBeSame, \ "FinalizationRegistry.prototype.register: target and holdings must not " \ "be same") \ - T(WeakRefsWeakRefConstructorTargetMustBeObject, \ - "WeakRef: target must be an object") \ + T(InvalidWeakRefsWeakRefConstructorTarget, "WeakRef: invalid target") \ T(OptionalChainingNoNew, "Invalid optional chain from new expression") \ T(OptionalChainingNoSuper, "Invalid optional chain from super property") \ T(OptionalChainingNoTemplate, "Invalid tagged template on optional chain") \ diff --git a/deps/v8/src/diagnostics/objects-debug.cc b/deps/v8/src/diagnostics/objects-debug.cc index 42e9dff7e363fd..e4cb23da28934b 100644 --- a/deps/v8/src/diagnostics/objects-debug.cc +++ b/deps/v8/src/diagnostics/objects-debug.cc @@ -1240,7 +1240,9 @@ void JSSharedStruct::JSSharedStructVerify(Isolate* isolate) { void WeakCell::WeakCellVerify(Isolate* isolate) { CHECK(IsWeakCell()); - CHECK(target().IsJSReceiver() || target().IsUndefined(isolate)); + CHECK(target().IsJSReceiver() || target().IsUndefined(isolate) || + (target().IsSymbol() && + !Symbol::cast(target()).is_in_public_symbol_table())); CHECK(prev().IsWeakCell() || prev().IsUndefined(isolate)); if (prev().IsWeakCell()) { @@ -1268,7 +1270,9 @@ void WeakCell::WeakCellVerify(Isolate* isolate) { void JSWeakRef::JSWeakRefVerify(Isolate* isolate) { CHECK(IsJSWeakRef()); JSObjectVerify(isolate); - CHECK(target().IsUndefined(isolate) || target().IsJSReceiver()); + CHECK(target().IsUndefined(isolate) || target().IsJSReceiver() || + (target().IsSymbol() && + !Symbol::cast(target()).is_in_public_symbol_table())); } void JSFinalizationRegistry::JSFinalizationRegistryVerify(Isolate* isolate) { diff --git a/deps/v8/src/flags/flag-definitions.h b/deps/v8/src/flags/flag-definitions.h index f02ef309d69e45..55e380a49e7ef5 100644 --- a/deps/v8/src/flags/flag-definitions.h +++ b/deps/v8/src/flags/flag-definitions.h @@ -307,7 +307,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_struct, "harmony structs and shared structs") + V(harmony_struct, "harmony structs and shared structs") \ + V(harmony_symbol_as_weakmap_key, "harmony symbols as weakmap keys") #ifdef V8_INTL_SUPPORT #define HARMONY_INPROGRESS(V) \ diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc index 02eefd9e4fdc78..53871476c72905 100644 --- a/deps/v8/src/heap/heap.cc +++ b/deps/v8/src/heap/heap.cc @@ -6924,7 +6924,7 @@ void Heap::RemoveDirtyFinalizationRegistriesOnContext(NativeContext context) { set_dirty_js_finalization_registries_list_tail(prev); } -void Heap::KeepDuringJob(Handle target) { +void Heap::KeepDuringJob(Handle target) { DCHECK(weak_refs_keep_during_job().IsUndefined() || weak_refs_keep_during_job().IsOrderedHashSet()); Handle table; diff --git a/deps/v8/src/heap/heap.h b/deps/v8/src/heap/heap.h index b0a8757ca96ab6..a3817f90ed3014 100644 --- a/deps/v8/src/heap/heap.h +++ b/deps/v8/src/heap/heap.h @@ -955,7 +955,7 @@ class Heap { return is_finalization_registry_cleanup_task_posted_; } - V8_EXPORT_PRIVATE void KeepDuringJob(Handle target); + V8_EXPORT_PRIVATE void KeepDuringJob(Handle target); void ClearKeptObjects(); // =========================================================================== diff --git a/deps/v8/src/init/bootstrapper.cc b/deps/v8/src/init/bootstrapper.cc index d4c4122c22f4d7..fd75d5faa317de 100644 --- a/deps/v8/src/init/bootstrapper.cc +++ b/deps/v8/src/init/bootstrapper.cc @@ -4484,6 +4484,7 @@ void Genesis::InitializeConsole(Handle extras_binding) { EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_assertions) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_private_brand_checks) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_static_blocks) +EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_symbol_as_weakmap_key) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_error_cause) #ifdef V8_INTL_SUPPORT diff --git a/deps/v8/src/objects/js-weak-refs-inl.h b/deps/v8/src/objects/js-weak-refs-inl.h index 76e6e075e5ded6..15f7d36b6fe72a 100644 --- a/deps/v8/src/objects/js-weak-refs-inl.h +++ b/deps/v8/src/objects/js-weak-refs-inl.h @@ -5,10 +5,9 @@ #ifndef V8_OBJECTS_JS_WEAK_REFS_INL_H_ #define V8_OBJECTS_JS_WEAK_REFS_INL_H_ -#include "src/objects/js-weak-refs.h" - #include "src/api/api-inl.h" #include "src/heap/heap-write-barrier-inl.h" +#include "src/objects/js-weak-refs.h" #include "src/objects/smi-inl.h" // Has to be the last include (doesn't have include guards): @@ -55,7 +54,7 @@ void JSFinalizationRegistry::RegisterWeakCellWithUnregisterToken( bool JSFinalizationRegistry::Unregister( Handle finalization_registry, - Handle unregister_token, Isolate* isolate) { + Handle unregister_token, Isolate* isolate) { // Iterate through the doubly linked list of WeakCells associated with the // key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of // its FinalizationRegistry; remove it from there. @@ -66,7 +65,7 @@ bool JSFinalizationRegistry::Unregister( template bool JSFinalizationRegistry::RemoveUnregisterToken( - JSReceiver unregister_token, Isolate* isolate, + HeapObject unregister_token, Isolate* isolate, RemoveUnregisterTokenMode removal_mode, GCNotifyUpdatedSlotCallback gc_notify_updated_slot) { // This method is called from both FinalizationRegistry#unregister and for @@ -171,7 +170,9 @@ void WeakCell::Nullify(Isolate* isolate, // only called for WeakCells which haven't been unregistered yet, so they will // be in the active_cells list. (The caller must guard against calling this // for unregistered WeakCells by checking that the target is not undefined.) - DCHECK(target().IsJSReceiver()); + DCHECK(target().IsJSReceiver() || + (target().IsSymbol() && + !Symbol::cast(target()).is_in_public_symbol_table())); set_target(ReadOnlyRoots(isolate).undefined_value()); JSFinalizationRegistry fr = diff --git a/deps/v8/src/objects/js-weak-refs.h b/deps/v8/src/objects/js-weak-refs.h index f678234ff81afc..64ff9573f6b2df 100644 --- a/deps/v8/src/objects/js-weak-refs.h +++ b/deps/v8/src/objects/js-weak-refs.h @@ -37,7 +37,7 @@ class JSFinalizationRegistry Handle weak_cell, Isolate* isolate); inline static bool Unregister( Handle finalization_registry, - Handle unregister_token, Isolate* isolate); + Handle unregister_token, Isolate* isolate); // RemoveUnregisterToken is called from both Unregister and during GC. Since // it modifies slots in key_map and WeakCells and the normal write barrier is @@ -49,7 +49,7 @@ class JSFinalizationRegistry }; template inline bool RemoveUnregisterToken( - JSReceiver unregister_token, Isolate* isolate, + HeapObject unregister_token, Isolate* isolate, RemoveUnregisterTokenMode removal_mode, GCNotifyUpdatedSlotCallback gc_notify_updated_slot); diff --git a/deps/v8/src/objects/js-weak-refs.tq b/deps/v8/src/objects/js-weak-refs.tq index c687ab50016776..c9fa8ee30bdb57 100644 --- a/deps/v8/src/objects/js-weak-refs.tq +++ b/deps/v8/src/objects/js-weak-refs.tq @@ -20,8 +20,8 @@ extern class JSFinalizationRegistry extends JSObject { extern class WeakCell extends HeapObject { finalization_registry: Undefined|JSFinalizationRegistry; - target: Undefined|JSReceiver; - unregister_token: Undefined|JSReceiver; + target: Undefined|JSReceiver|Symbol; + unregister_token: Undefined|JSReceiver|Symbol; holdings: JSAny; // For storing doubly linked lists of WeakCells in JSFinalizationRegistry's @@ -38,5 +38,6 @@ extern class WeakCell extends HeapObject { key_list_prev: Undefined|WeakCell; key_list_next: Undefined|WeakCell; } - -extern class JSWeakRef extends JSObject { target: Undefined|JSReceiver; } +extern class JSWeakRef extends JSObject { + target: Undefined|JSReceiver|Symbol; +} diff --git a/deps/v8/src/runtime/runtime-weak-refs.cc b/deps/v8/src/runtime/runtime-weak-refs.cc index f3c6f63ebcb6d7..db3611dbb7d4eb 100644 --- a/deps/v8/src/runtime/runtime-weak-refs.cc +++ b/deps/v8/src/runtime/runtime-weak-refs.cc @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "src/runtime/runtime-utils.h" - #include "src/execution/arguments-inl.h" #include "src/objects/js-weak-refs-inl.h" +#include "src/runtime/runtime-utils.h" namespace v8 { namespace internal { @@ -44,7 +43,14 @@ RUNTIME_FUNCTION( RUNTIME_FUNCTION(Runtime_JSWeakRefAddToKeptObjects) { HandleScope scope(isolate); DCHECK_EQ(1, args.length()); - Handle object = args.at(0); + Handle object = args.at(0); + if (FLAG_harmony_symbol_as_weakmap_key) { + DCHECK(object->IsJSReceiver() || + (object->IsSymbol() && + !(Handle::cast(object))->is_in_public_symbol_table())); + } else { + DCHECK(object->IsJSReceiver()); + } isolate->heap()->KeepDuringJob(object); diff --git a/deps/v8/test/message/fail/weak-refs-register1.out b/deps/v8/test/message/fail/weak-refs-register1.out index aa4cbc2fa22e1e..7e7bf12791be2d 100644 --- a/deps/v8/test/message/fail/weak-refs-register1.out +++ b/deps/v8/test/message/fail/weak-refs-register1.out @@ -1,6 +1,6 @@ -*%(basename)s:*: TypeError: FinalizationRegistry.prototype.register: target must be an object +*%(basename)s:*: TypeError: FinalizationRegistry.prototype.register: invalid target fg.register(1); ^ -TypeError: FinalizationRegistry.prototype.register: target must be an object +TypeError: FinalizationRegistry.prototype.register: invalid target at FinalizationRegistry.register () at *%(basename)s:*:4 diff --git a/deps/v8/test/message/fail/weak-refs-unregister.out b/deps/v8/test/message/fail/weak-refs-unregister.out index 52945869838778..a6a66ea709d747 100644 --- a/deps/v8/test/message/fail/weak-refs-unregister.out +++ b/deps/v8/test/message/fail/weak-refs-unregister.out @@ -1,6 +1,6 @@ -*%(basename)s:*: TypeError: unregisterToken ('1') must be an object +*%(basename)s:*: TypeError: Invalid unregisterToken ('1') fg.unregister(1); ^ -TypeError: unregisterToken ('1') must be an object +TypeError: Invalid unregisterToken ('1') at FinalizationRegistry.unregister () at *%(basename)s:*:4 diff --git a/deps/v8/test/mjsunit/harmony/symbol-as-weakmap-key.js b/deps/v8/test/mjsunit/harmony/symbol-as-weakmap-key.js new file mode 100644 index 00000000000000..0e97ea1accca58 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/symbol-as-weakmap-key.js @@ -0,0 +1,99 @@ +// 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: --harmony-symbol-as-weakmap-key --expose-gc + +(function TestWeakMapWithNonRegisteredSymbolKey() { + const key = Symbol('123'); + const value = 1; + const map = new WeakMap(); + assertFalse(map.has(key)); + assertSame(undefined, map.get(key)); + assertFalse(map.delete(key)); + assertSame(map, map.set(key, value)); + assertSame(value, map.get(key)); + assertTrue(map.has(key)); + assertTrue(map.delete(key)); + assertFalse(map.has(key)); + assertSame(undefined, map.get(key)); + assertFalse(map.delete(key)); + assertFalse(map.has(key)); + assertSame(undefined, map.get(key)); +})(); + +(function TestWeakMapWithNonRegisteredSymbolKeyGC() { + const map = new WeakMap(); + + const outerKey = Symbol('234'); + const outerValue = 1; + map.set(outerKey, outerValue); + { + const innerKey = Symbol('123'); + const innerValue = 1; + map.set(innerKey, innerValue); + assertTrue(map.has(innerKey)); + assertSame(innerValue, map.get(innerKey)); + } + gc(); + assertTrue(map.has(outerKey)); + assertSame(outerValue, map.get(outerKey)); +})(); + +(function TestWeakMapWithRegisteredSymbolKey() { + const key = Symbol.for('123'); + const value = 1; + const map = new WeakMap(); + assertFalse(map.has(key)); + assertSame(undefined, map.get(key)); + assertFalse(map.delete(key)); + assertThrows(() => { + map.set(key, value); + }, TypeError, 'Invalid value used as weak map key'); + assertFalse(map.has(key)); + assertSame(undefined, map.get(key)); + assertFalse(map.delete(key)); + assertFalse(map.has(key)); + assertSame(undefined, map.get(key)); +})(); + +(function TestWeakSetWithNonRegisteredSymbolKey() { + const key = Symbol('123'); + const set = new WeakSet(); + assertFalse(set.has(key)); + assertFalse(set.delete(key)); + assertSame(set, set.add(key)); + assertTrue(set.has(key)); + assertTrue(set.delete(key)); + assertFalse(set.has(key)); + assertFalse(set.delete(key)); + assertFalse(set.has(key)); +})(); + +(function TestWeakSetWithNonRegisteredSymbolKeyGC() { + const set = new WeakSet(); + const outerKey = Symbol('234'); + set.add(outerKey); + { + const innerKey = Symbol('123'); + set.add(innerKey); + assertTrue(set.has(innerKey)); + } + gc(); + assertTrue(set.has(outerKey)); +})(); + +(function TestWeakSetWithRegisteredSymbolKey() { + const key = Symbol.for('123'); + const set = new WeakSet(); + assertFalse(set.has(key)); + assertFalse(set.delete(key)); + + assertThrows(() => { + assertSame(set, set.add(key)); + }, TypeError, 'Invalid value used in weak set'); + + assertFalse(set.has(key)); + assertFalse(set.delete(key)); + assertFalse(set.has(key)); +})(); diff --git a/deps/v8/test/mjsunit/harmony/weakrefs/basics.js b/deps/v8/test/mjsunit/harmony/weakrefs/basics.js index f879df9a2a63b0..7628c641bc7984 100644 --- a/deps/v8/test/mjsunit/harmony/weakrefs/basics.js +++ b/deps/v8/test/mjsunit/harmony/weakrefs/basics.js @@ -47,7 +47,7 @@ (function TestRegisterWithNonObjectTarget() { let fg = new FinalizationRegistry(() => {}); - let message = "FinalizationRegistry.prototype.register: target must be an object"; + let message = "FinalizationRegistry.prototype.register: invalid target"; assertThrows(() => fg.register(1, "holdings"), TypeError, message); assertThrows(() => fg.register(false, "holdings"), TypeError, message); assertThrows(() => fg.register("foo", "holdings"), TypeError, message); @@ -116,7 +116,7 @@ })(); (function TestWeakRefConstructorWithNonObject() { - let message = "WeakRef: target must be an object"; + let message = "WeakRef: invalid target"; assertThrows(() => new WeakRef(), TypeError, message); assertThrows(() => new WeakRef(1), TypeError, message); assertThrows(() => new WeakRef(false), TypeError, message); diff --git a/deps/v8/test/mjsunit/harmony/weakrefs/symbol-as-weakref-target-gc.js b/deps/v8/test/mjsunit/harmony/weakrefs/symbol-as-weakref-target-gc.js new file mode 100644 index 00000000000000..561bf4f058bdd7 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/weakrefs/symbol-as-weakref-target-gc.js @@ -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: --harmony-symbol-as-weakmap-key --expose-gc --noincremental-marking + +(function TestWeakRefWithSymbolGC() { + let weakRef; + { + const innerKey = Symbol('123'); + weakRef = new WeakRef(innerKey); + } + // Since the WeakRef was created during this turn, it is not cleared by GC. + gc(); + assertNotEquals(undefined, weakRef.deref()); + // Next task. + setTimeout(() => { + gc(); + assertEquals(undefined, weakRef.deref()); + }, 0); +})(); + +(function TestFinalizationRegistryWithSymbolGC() { + let cleanUpCalled = false; + const fg = new FinalizationRegistry((target) => { + assertEquals('123', target); + cleanUpCalled = true; + }); + (function () { + const innerKey = Symbol('123'); + fg.register(innerKey, '123'); + })(); + gc(); + assertFalse(cleanUpCalled); + // Check that cleanup callback was called in a follow up task. + setTimeout(() => { + assertTrue(cleanUpCalled); + }, 0); +})(); diff --git a/deps/v8/test/mjsunit/harmony/weakrefs/symbol-as-weakref-target.js b/deps/v8/test/mjsunit/harmony/weakrefs/symbol-as-weakref-target.js new file mode 100644 index 00000000000000..1dc874ed83b3da --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/weakrefs/symbol-as-weakref-target.js @@ -0,0 +1,41 @@ +// 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: --harmony-symbol-as-weakmap-key + +(function TestRegisterWithSymbolTarget() { + const fg = new FinalizationRegistry(() => { }); + fg.register(Symbol('123'), 'holdings'); + // Registered symbols cannot be the target. + assertThrows(() => fg.register(Symbol.for('123'), 'holdings'), TypeError); +})(); + +(function TestRegisterWithSymbolUnregisterToken() { + const fg = new FinalizationRegistry(() => { }); + fg.register({}, 'holdings', Symbol('123')); + // Registered symbols cannot be the unregister token. + assertThrows(() => fg.register({}, 'holdings', Symbol.for('123')), TypeError); +})(); + +(function TestRegisterSymbolAndHoldingsSameValue() { + const fg = new FinalizationRegistry(() => {}); + const obj = Symbol('123'); + // SameValue(target, holdings) not ok. + assertThrows(() => fg.register(obj, obj), TypeError); + const holdings = {a: 1}; + fg.register(obj, holdings); +})(); + +(function TestUnregisterWithSymbolUnregisterToken() { + const fg = new FinalizationRegistry(() => {}); + fg.unregister(Symbol('123')); + // Registered symbols cannot be the unregister token. + assertThrows(() => fg.unregister(Symbol.for('123')), TypeError); +})(); + +(function TestWeakRefConstructorWithSymbol() { + new WeakRef(Symbol('123')); + // Registered symbols cannot be the WeakRef target. + assertThrows(() => new WeakRef(Symbol.for('123')), TypeError); +})();