Skip to content

Commit 1ce901b

Browse files
joyeecheungrichardlau
authored andcommittedMar 15, 2024
deps: V8: cherry-pick c400af48b5ef
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 <dinfuehr@chromium.org> Commit-Queue: Marja Hölttä <marja@chromium.org> Reviewed-by: Jakob Linke <jgruber@chromium.org> Cr-Commit-Position: refs/heads/main@{#83313} Refs: v8/v8@c400af4 PR-URL: #51004 Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 902d8b3 commit 1ce901b

27 files changed

+534
-261
lines changed
 

‎common.gypi

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
# Reset this number to 0 on major V8 upgrades.
3838
# Increment by one for each non-official patch applied to deps/v8.
39-
'v8_embedder_string': '-node.28',
39+
'v8_embedder_string': '-node.29',
4040

4141
##### V8 defaults for Node.js #####
4242

‎deps/v8/src/builtins/base.tq

+5-5
Original file line numberDiff line numberDiff line change
@@ -438,9 +438,9 @@ extern enum MessageTemplate {
438438
kWasmTrapArrayOutOfBounds,
439439
kWasmTrapArrayTooLarge,
440440
kWeakRefsRegisterTargetAndHoldingsMustNotBeSame,
441-
kWeakRefsRegisterTargetMustBeObject,
442-
kWeakRefsUnregisterTokenMustBeObject,
443-
kWeakRefsWeakRefConstructorTargetMustBeObject,
441+
kInvalidWeakRefsRegisterTarget,
442+
kInvalidWeakRefsUnregisterToken,
443+
kInvalidWeakRefsWeakRefConstructorTarget,
444444
...
445445
}
446446

@@ -906,10 +906,10 @@ macro Float64IsNaN(n: float64): bool {
906906
// The type of all tagged values that can safely be compared with TaggedEqual.
907907
@if(V8_ENABLE_WEBASSEMBLY)
908908
type TaggedWithIdentity = JSReceiver | FixedArrayBase | Oddball | Map |
909-
WeakCell | Context | EmptyString | WasmInternalFunction;
909+
WeakCell | Context | EmptyString | Symbol | WasmInternalFunction;
910910
@ifnot(V8_ENABLE_WEBASSEMBLY)
911911
type TaggedWithIdentity = JSReceiver | FixedArrayBase | Oddball | Map |
912-
WeakCell | Context | EmptyString;
912+
WeakCell | Context | EmptyString | Symbol;
913913

914914
extern operator '==' macro TaggedEqual(TaggedWithIdentity, Object): bool;
915915
extern operator '==' macro TaggedEqual(Object, TaggedWithIdentity): bool;

‎deps/v8/src/builtins/builtins-collections-gen.cc

+37-193
Original file line numberDiff line numberDiff line change
@@ -22,130 +22,6 @@ namespace internal {
2222
template <class T>
2323
using TVariable = compiler::TypedCodeAssemblerVariable<T>;
2424

25-
class BaseCollectionsAssembler : public CodeStubAssembler {
26-
public:
27-
explicit BaseCollectionsAssembler(compiler::CodeAssemblerState* state)
28-
: CodeStubAssembler(state) {}
29-
30-
virtual ~BaseCollectionsAssembler() = default;
31-
32-
protected:
33-
enum Variant { kMap, kSet, kWeakMap, kWeakSet };
34-
35-
// Adds an entry to a collection. For Maps, properly handles extracting the
36-
// key and value from the entry (see LoadKeyValue()).
37-
void AddConstructorEntry(Variant variant, TNode<Context> context,
38-
TNode<Object> collection, TNode<Object> add_function,
39-
TNode<Object> key_value,
40-
Label* if_may_have_side_effects = nullptr,
41-
Label* if_exception = nullptr,
42-
TVariable<Object>* var_exception = nullptr);
43-
44-
// Adds constructor entries to a collection. Choosing a fast path when
45-
// possible.
46-
void AddConstructorEntries(Variant variant, TNode<Context> context,
47-
TNode<Context> native_context,
48-
TNode<HeapObject> collection,
49-
TNode<Object> initial_entries);
50-
51-
// Fast path for adding constructor entries. Assumes the entries are a fast
52-
// JS array (see CodeStubAssembler::BranchIfFastJSArray()).
53-
void AddConstructorEntriesFromFastJSArray(Variant variant,
54-
TNode<Context> context,
55-
TNode<Context> native_context,
56-
TNode<Object> collection,
57-
TNode<JSArray> fast_jsarray,
58-
Label* if_may_have_side_effects);
59-
60-
// Adds constructor entries to a collection using the iterator protocol.
61-
void AddConstructorEntriesFromIterable(Variant variant,
62-
TNode<Context> context,
63-
TNode<Context> native_context,
64-
TNode<Object> collection,
65-
TNode<Object> iterable);
66-
67-
// Constructs a collection instance. Choosing a fast path when possible.
68-
TNode<JSObject> AllocateJSCollection(TNode<Context> context,
69-
TNode<JSFunction> constructor,
70-
TNode<JSReceiver> new_target);
71-
72-
// Fast path for constructing a collection instance if the constructor
73-
// function has not been modified.
74-
TNode<JSObject> AllocateJSCollectionFast(TNode<JSFunction> constructor);
75-
76-
// Fallback for constructing a collection instance if the constructor function
77-
// has been modified.
78-
TNode<JSObject> AllocateJSCollectionSlow(TNode<Context> context,
79-
TNode<JSFunction> constructor,
80-
TNode<JSReceiver> new_target);
81-
82-
// Allocates the backing store for a collection.
83-
virtual TNode<HeapObject> AllocateTable(
84-
Variant variant, TNode<IntPtrT> at_least_space_for) = 0;
85-
86-
// Main entry point for a collection constructor builtin.
87-
void GenerateConstructor(Variant variant,
88-
Handle<String> constructor_function_name,
89-
TNode<Object> new_target, TNode<IntPtrT> argc,
90-
TNode<Context> context);
91-
92-
// Retrieves the collection function that adds an entry. `set` for Maps and
93-
// `add` for Sets.
94-
TNode<Object> GetAddFunction(Variant variant, TNode<Context> context,
95-
TNode<Object> collection);
96-
97-
// Retrieves the collection constructor function.
98-
TNode<JSFunction> GetConstructor(Variant variant,
99-
TNode<Context> native_context);
100-
101-
// Retrieves the initial collection function that adds an entry. Should only
102-
// be called when it is certain that a collection prototype's map hasn't been
103-
// changed.
104-
TNode<JSFunction> GetInitialAddFunction(Variant variant,
105-
TNode<Context> native_context);
106-
107-
// Checks whether {collection}'s initial add/set function has been modified
108-
// (depending on {variant}, loaded from {native_context}).
109-
void GotoIfInitialAddFunctionModified(Variant variant,
110-
TNode<NativeContext> native_context,
111-
TNode<HeapObject> collection,
112-
Label* if_modified);
113-
114-
// Gets root index for the name of the add/set function.
115-
RootIndex GetAddFunctionNameIndex(Variant variant);
116-
117-
// Retrieves the offset to access the backing table from the collection.
118-
int GetTableOffset(Variant variant);
119-
120-
// Estimates the number of entries the collection will have after adding the
121-
// entries passed in the constructor. AllocateTable() can use this to avoid
122-
// the time of growing/rehashing when adding the constructor entries.
123-
TNode<IntPtrT> EstimatedInitialSize(TNode<Object> initial_entries,
124-
TNode<BoolT> is_fast_jsarray);
125-
126-
void GotoIfCannotBeWeakKey(const TNode<Object> obj,
127-
Label* if_cannot_be_weak_key);
128-
129-
// Determines whether the collection's prototype has been modified.
130-
TNode<BoolT> HasInitialCollectionPrototype(Variant variant,
131-
TNode<Context> native_context,
132-
TNode<Object> collection);
133-
134-
// Gets the initial prototype map for given collection {variant}.
135-
TNode<Map> GetInitialCollectionPrototype(Variant variant,
136-
TNode<Context> native_context);
137-
138-
// Loads an element from a fixed array. If the element is the hole, returns
139-
// `undefined`.
140-
TNode<Object> LoadAndNormalizeFixedArrayElement(TNode<FixedArray> elements,
141-
TNode<IntPtrT> index);
142-
143-
// Loads an element from a fixed double array. If the element is the hole,
144-
// returns `undefined`.
145-
TNode<Object> LoadAndNormalizeFixedDoubleArrayElement(
146-
TNode<HeapObject> elements, TNode<IntPtrT> index);
147-
};
148-
14925
void BaseCollectionsAssembler::AddConstructorEntry(
15026
Variant variant, TNode<Context> context, TNode<Object> collection,
15127
TNode<Object> add_function, TNode<Object> key_value,
@@ -525,12 +401,23 @@ TNode<IntPtrT> BaseCollectionsAssembler::EstimatedInitialSize(
525401

526402
void BaseCollectionsAssembler::GotoIfCannotBeWeakKey(
527403
const TNode<Object> obj, Label* if_cannot_be_weak_key) {
404+
Label check_symbol_key(this);
405+
Label end(this);
528406
GotoIf(TaggedIsSmi(obj), if_cannot_be_weak_key);
529407
TNode<Uint16T> instance_type = LoadMapInstanceType(LoadMap(CAST(obj)));
530-
GotoIfNot(IsJSReceiverInstanceType(instance_type), if_cannot_be_weak_key);
408+
GotoIfNot(IsJSReceiverInstanceType(instance_type), &check_symbol_key);
531409
// TODO(v8:12547) Shared structs should only be able to point to shared values
532410
// in weak collections. For now, disallow them as weak collection keys.
533411
GotoIf(IsJSSharedStructInstanceType(instance_type), if_cannot_be_weak_key);
412+
Goto(&end);
413+
Bind(&check_symbol_key);
414+
GotoIfNot(HasHarmonySymbolAsWeakmapKeyFlag(), if_cannot_be_weak_key);
415+
GotoIfNot(IsSymbolInstanceType(instance_type), if_cannot_be_weak_key);
416+
TNode<Uint32T> flags = LoadSymbolFlags(CAST(obj));
417+
GotoIf(Word32And(flags, Symbol::IsInPublicSymbolTableBit::kMask),
418+
if_cannot_be_weak_key);
419+
Goto(&end);
420+
Bind(&end);
534421
}
535422

536423
TNode<Map> BaseCollectionsAssembler::GetInitialCollectionPrototype(
@@ -2427,67 +2314,6 @@ TF_BUILTIN(FindOrderedHashMapEntry, CollectionsBuiltinsAssembler) {
24272314
Return(SmiConstant(-1));
24282315
}
24292316

2430-
class WeakCollectionsBuiltinsAssembler : public BaseCollectionsAssembler {
2431-
public:
2432-
explicit WeakCollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state)
2433-
: BaseCollectionsAssembler(state) {}
2434-
2435-
protected:
2436-
void AddEntry(TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
2437-
TNode<Object> key, TNode<Object> value,
2438-
TNode<IntPtrT> number_of_elements);
2439-
2440-
TNode<HeapObject> AllocateTable(Variant variant,
2441-
TNode<IntPtrT> at_least_space_for) override;
2442-
2443-
// Generates and sets the identity for a JSRececiver.
2444-
TNode<Smi> CreateIdentityHash(TNode<Object> receiver);
2445-
TNode<IntPtrT> EntryMask(TNode<IntPtrT> capacity);
2446-
2447-
// Builds code that finds the EphemeronHashTable entry for a {key} using the
2448-
// comparison code generated by {key_compare}. The key index is returned if
2449-
// the {key} is found.
2450-
using KeyComparator =
2451-
std::function<void(TNode<Object> entry_key, Label* if_same)>;
2452-
TNode<IntPtrT> FindKeyIndex(TNode<HeapObject> table, TNode<IntPtrT> key_hash,
2453-
TNode<IntPtrT> entry_mask,
2454-
const KeyComparator& key_compare);
2455-
2456-
// Builds code that finds an EphemeronHashTable entry available for a new
2457-
// entry.
2458-
TNode<IntPtrT> FindKeyIndexForInsertion(TNode<HeapObject> table,
2459-
TNode<IntPtrT> key_hash,
2460-
TNode<IntPtrT> entry_mask);
2461-
2462-
// Builds code that finds the EphemeronHashTable entry with key that matches
2463-
// {key} and returns the entry's key index. If {key} cannot be found, jumps to
2464-
// {if_not_found}.
2465-
TNode<IntPtrT> FindKeyIndexForKey(TNode<HeapObject> table, TNode<Object> key,
2466-
TNode<IntPtrT> hash,
2467-
TNode<IntPtrT> entry_mask,
2468-
Label* if_not_found);
2469-
2470-
TNode<Word32T> InsufficientCapacityToAdd(TNode<IntPtrT> capacity,
2471-
TNode<IntPtrT> number_of_elements,
2472-
TNode<IntPtrT> number_of_deleted);
2473-
TNode<IntPtrT> KeyIndexFromEntry(TNode<IntPtrT> entry);
2474-
2475-
TNode<IntPtrT> LoadNumberOfElements(TNode<EphemeronHashTable> table,
2476-
int offset);
2477-
TNode<IntPtrT> LoadNumberOfDeleted(TNode<EphemeronHashTable> table,
2478-
int offset = 0);
2479-
TNode<EphemeronHashTable> LoadTable(TNode<JSWeakCollection> collection);
2480-
TNode<IntPtrT> LoadTableCapacity(TNode<EphemeronHashTable> table);
2481-
2482-
void RemoveEntry(TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
2483-
TNode<IntPtrT> number_of_elements);
2484-
TNode<BoolT> ShouldRehash(TNode<IntPtrT> number_of_elements,
2485-
TNode<IntPtrT> number_of_deleted);
2486-
TNode<Word32T> ShouldShrink(TNode<IntPtrT> capacity,
2487-
TNode<IntPtrT> number_of_elements);
2488-
TNode<IntPtrT> ValueIndexFromKeyIndex(TNode<IntPtrT> key_index);
2489-
};
2490-
24912317
void WeakCollectionsBuiltinsAssembler::AddEntry(
24922318
TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
24932319
TNode<Object> key, TNode<Object> value, TNode<IntPtrT> number_of_elements) {
@@ -2503,6 +2329,25 @@ void WeakCollectionsBuiltinsAssembler::AddEntry(
25032329
SmiFromIntPtr(number_of_elements));
25042330
}
25052331

2332+
TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::GetHash(
2333+
const TNode<HeapObject> key, Label* if_no_hash) {
2334+
TVARIABLE(IntPtrT, var_hash);
2335+
Label if_symbol(this);
2336+
Label return_result(this);
2337+
GotoIfNot(IsJSReceiver(key), &if_symbol);
2338+
var_hash = LoadJSReceiverIdentityHash(CAST(key), if_no_hash);
2339+
Goto(&return_result);
2340+
Bind(&if_symbol);
2341+
CSA_DCHECK(this, IsSymbol(key));
2342+
CSA_DCHECK(this, Word32BinaryNot(
2343+
Word32And(LoadSymbolFlags(CAST(key)),
2344+
Symbol::IsInPublicSymbolTableBit::kMask)));
2345+
var_hash = ChangeInt32ToIntPtr(LoadNameHash(CAST(key), nullptr));
2346+
Goto(&return_result);
2347+
Bind(&return_result);
2348+
return var_hash.value();
2349+
}
2350+
25062351
TNode<HeapObject> WeakCollectionsBuiltinsAssembler::AllocateTable(
25072352
Variant variant, TNode<IntPtrT> at_least_space_for) {
25082353
// See HashTable::New().
@@ -2732,8 +2577,7 @@ TF_BUILTIN(WeakMapLookupHashIndex, WeakCollectionsBuiltinsAssembler) {
27322577

27332578
GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);
27342579

2735-
TNode<IntPtrT> hash =
2736-
LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
2580+
TNode<IntPtrT> hash = GetHash(CAST(key), &if_cannot_be_weak_key);
27372581
TNode<IntPtrT> capacity = LoadTableCapacity(table);
27382582
TNode<IntPtrT> key_index = FindKeyIndexForKey(
27392583
table, key, hash, EntryMask(capacity), &if_cannot_be_weak_key);
@@ -2798,8 +2642,7 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {
27982642

27992643
GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);
28002644

2801-
TNode<IntPtrT> hash =
2802-
LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
2645+
TNode<IntPtrT> hash = GetHash(CAST(key), &if_cannot_be_weak_key);
28032646
TNode<EphemeronHashTable> table = LoadTable(collection);
28042647
TNode<IntPtrT> capacity = LoadTableCapacity(table);
28052648
TNode<IntPtrT> key_index = FindKeyIndexForKey(
@@ -2823,18 +2666,18 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {
28232666
TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) {
28242667
auto context = Parameter<Context>(Descriptor::kContext);
28252668
auto collection = Parameter<JSWeakCollection>(Descriptor::kCollection);
2826-
auto key = Parameter<JSReceiver>(Descriptor::kKey);
2669+
auto key = Parameter<HeapObject>(Descriptor::kKey);
28272670
auto value = Parameter<Object>(Descriptor::kValue);
28282671

2829-
CSA_DCHECK(this, IsJSReceiver(key));
2672+
CSA_DCHECK(this, Word32Or(IsJSReceiver(key), IsSymbol(key)));
28302673

28312674
Label call_runtime(this), if_no_hash(this), if_not_found(this);
28322675

28332676
TNode<EphemeronHashTable> table = LoadTable(collection);
28342677
TNode<IntPtrT> capacity = LoadTableCapacity(table);
28352678
TNode<IntPtrT> entry_mask = EntryMask(capacity);
28362679

2837-
TVARIABLE(IntPtrT, var_hash, LoadJSReceiverIdentityHash(key, &if_no_hash));
2680+
TVARIABLE(IntPtrT, var_hash, GetHash(key, &if_no_hash));
28382681
TNode<IntPtrT> key_index = FindKeyIndexForKey(table, key, var_hash.value(),
28392682
entry_mask, &if_not_found);
28402683

@@ -2843,6 +2686,7 @@ TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) {
28432686

28442687
BIND(&if_no_hash);
28452688
{
2689+
CSA_DCHECK(this, IsJSReceiver(key));
28462690
var_hash = SmiUntag(CreateIdentityHash(key));
28472691
Goto(&if_not_found);
28482692
}

‎deps/v8/src/builtins/builtins-collections-gen.h

+186
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,192 @@ void BranchIfIterableWithOriginalValueSetIterator(
2020
TNode<Context> context, compiler::CodeAssemblerLabel* if_true,
2121
compiler::CodeAssemblerLabel* if_false);
2222

23+
class BaseCollectionsAssembler : public CodeStubAssembler {
24+
public:
25+
explicit BaseCollectionsAssembler(compiler::CodeAssemblerState* state)
26+
: CodeStubAssembler(state) {}
27+
28+
virtual ~BaseCollectionsAssembler() = default;
29+
30+
void GotoIfCannotBeWeakKey(const TNode<Object> obj,
31+
Label* if_cannot_be_weak_key);
32+
33+
protected:
34+
enum Variant { kMap, kSet, kWeakMap, kWeakSet };
35+
36+
// Adds an entry to a collection. For Maps, properly handles extracting the
37+
// key and value from the entry (see LoadKeyValue()).
38+
void AddConstructorEntry(Variant variant, TNode<Context> context,
39+
TNode<Object> collection, TNode<Object> add_function,
40+
TNode<Object> key_value,
41+
Label* if_may_have_side_effects = nullptr,
42+
Label* if_exception = nullptr,
43+
TVariable<Object>* var_exception = nullptr);
44+
45+
// Adds constructor entries to a collection. Choosing a fast path when
46+
// possible.
47+
void AddConstructorEntries(Variant variant, TNode<Context> context,
48+
TNode<Context> native_context,
49+
TNode<HeapObject> collection,
50+
TNode<Object> initial_entries);
51+
52+
// Fast path for adding constructor entries. Assumes the entries are a fast
53+
// JS array (see CodeStubAssembler::BranchIfFastJSArray()).
54+
void AddConstructorEntriesFromFastJSArray(Variant variant,
55+
TNode<Context> context,
56+
TNode<Context> native_context,
57+
TNode<Object> collection,
58+
TNode<JSArray> fast_jsarray,
59+
Label* if_may_have_side_effects);
60+
61+
// Adds constructor entries to a collection using the iterator protocol.
62+
void AddConstructorEntriesFromIterable(Variant variant,
63+
TNode<Context> context,
64+
TNode<Context> native_context,
65+
TNode<Object> collection,
66+
TNode<Object> iterable);
67+
68+
// Constructs a collection instance. Choosing a fast path when possible.
69+
TNode<JSObject> AllocateJSCollection(TNode<Context> context,
70+
TNode<JSFunction> constructor,
71+
TNode<JSReceiver> new_target);
72+
73+
// Fast path for constructing a collection instance if the constructor
74+
// function has not been modified.
75+
TNode<JSObject> AllocateJSCollectionFast(TNode<JSFunction> constructor);
76+
77+
// Fallback for constructing a collection instance if the constructor function
78+
// has been modified.
79+
TNode<JSObject> AllocateJSCollectionSlow(TNode<Context> context,
80+
TNode<JSFunction> constructor,
81+
TNode<JSReceiver> new_target);
82+
83+
// Allocates the backing store for a collection.
84+
virtual TNode<HeapObject> AllocateTable(
85+
Variant variant, TNode<IntPtrT> at_least_space_for) = 0;
86+
87+
// Main entry point for a collection constructor builtin.
88+
void GenerateConstructor(Variant variant,
89+
Handle<String> constructor_function_name,
90+
TNode<Object> new_target, TNode<IntPtrT> argc,
91+
TNode<Context> context);
92+
93+
// Retrieves the collection function that adds an entry. `set` for Maps and
94+
// `add` for Sets.
95+
TNode<Object> GetAddFunction(Variant variant, TNode<Context> context,
96+
TNode<Object> collection);
97+
98+
// Retrieves the collection constructor function.
99+
TNode<JSFunction> GetConstructor(Variant variant,
100+
TNode<Context> native_context);
101+
102+
// Retrieves the initial collection function that adds an entry. Should only
103+
// be called when it is certain that a collection prototype's map hasn't been
104+
// changed.
105+
TNode<JSFunction> GetInitialAddFunction(Variant variant,
106+
TNode<Context> native_context);
107+
108+
// Checks whether {collection}'s initial add/set function has been modified
109+
// (depending on {variant}, loaded from {native_context}).
110+
void GotoIfInitialAddFunctionModified(Variant variant,
111+
TNode<NativeContext> native_context,
112+
TNode<HeapObject> collection,
113+
Label* if_modified);
114+
115+
// Gets root index for the name of the add/set function.
116+
RootIndex GetAddFunctionNameIndex(Variant variant);
117+
118+
// Retrieves the offset to access the backing table from the collection.
119+
int GetTableOffset(Variant variant);
120+
121+
// Estimates the number of entries the collection will have after adding the
122+
// entries passed in the constructor. AllocateTable() can use this to avoid
123+
// the time of growing/rehashing when adding the constructor entries.
124+
TNode<IntPtrT> EstimatedInitialSize(TNode<Object> initial_entries,
125+
TNode<BoolT> is_fast_jsarray);
126+
127+
// Determines whether the collection's prototype has been modified.
128+
TNode<BoolT> HasInitialCollectionPrototype(Variant variant,
129+
TNode<Context> native_context,
130+
TNode<Object> collection);
131+
132+
// Gets the initial prototype map for given collection {variant}.
133+
TNode<Map> GetInitialCollectionPrototype(Variant variant,
134+
TNode<Context> native_context);
135+
136+
// Loads an element from a fixed array. If the element is the hole, returns
137+
// `undefined`.
138+
TNode<Object> LoadAndNormalizeFixedArrayElement(TNode<FixedArray> elements,
139+
TNode<IntPtrT> index);
140+
141+
// Loads an element from a fixed double array. If the element is the hole,
142+
// returns `undefined`.
143+
TNode<Object> LoadAndNormalizeFixedDoubleArrayElement(
144+
TNode<HeapObject> elements, TNode<IntPtrT> index);
145+
};
146+
147+
class WeakCollectionsBuiltinsAssembler : public BaseCollectionsAssembler {
148+
public:
149+
explicit WeakCollectionsBuiltinsAssembler(compiler::CodeAssemblerState* state)
150+
: BaseCollectionsAssembler(state) {}
151+
152+
protected:
153+
void AddEntry(TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
154+
TNode<Object> key, TNode<Object> value,
155+
TNode<IntPtrT> number_of_elements);
156+
157+
TNode<HeapObject> AllocateTable(Variant variant,
158+
TNode<IntPtrT> at_least_space_for) override;
159+
160+
TNode<IntPtrT> GetHash(const TNode<HeapObject> key, Label* if_no_hash);
161+
// Generates and sets the identity for a JSRececiver.
162+
TNode<Smi> CreateIdentityHash(TNode<Object> receiver);
163+
TNode<IntPtrT> EntryMask(TNode<IntPtrT> capacity);
164+
165+
// Builds code that finds the EphemeronHashTable entry for a {key} using the
166+
// comparison code generated by {key_compare}. The key index is returned if
167+
// the {key} is found.
168+
using KeyComparator =
169+
std::function<void(TNode<Object> entry_key, Label* if_same)>;
170+
TNode<IntPtrT> FindKeyIndex(TNode<HeapObject> table, TNode<IntPtrT> key_hash,
171+
TNode<IntPtrT> entry_mask,
172+
const KeyComparator& key_compare);
173+
174+
// Builds code that finds an EphemeronHashTable entry available for a new
175+
// entry.
176+
TNode<IntPtrT> FindKeyIndexForInsertion(TNode<HeapObject> table,
177+
TNode<IntPtrT> key_hash,
178+
TNode<IntPtrT> entry_mask);
179+
180+
// Builds code that finds the EphemeronHashTable entry with key that matches
181+
// {key} and returns the entry's key index. If {key} cannot be found, jumps to
182+
// {if_not_found}.
183+
TNode<IntPtrT> FindKeyIndexForKey(TNode<HeapObject> table, TNode<Object> key,
184+
TNode<IntPtrT> hash,
185+
TNode<IntPtrT> entry_mask,
186+
Label* if_not_found);
187+
188+
TNode<Word32T> InsufficientCapacityToAdd(TNode<IntPtrT> capacity,
189+
TNode<IntPtrT> number_of_elements,
190+
TNode<IntPtrT> number_of_deleted);
191+
TNode<IntPtrT> KeyIndexFromEntry(TNode<IntPtrT> entry);
192+
193+
TNode<IntPtrT> LoadNumberOfElements(TNode<EphemeronHashTable> table,
194+
int offset);
195+
TNode<IntPtrT> LoadNumberOfDeleted(TNode<EphemeronHashTable> table,
196+
int offset = 0);
197+
TNode<EphemeronHashTable> LoadTable(TNode<JSWeakCollection> collection);
198+
TNode<IntPtrT> LoadTableCapacity(TNode<EphemeronHashTable> table);
199+
200+
void RemoveEntry(TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
201+
TNode<IntPtrT> number_of_elements);
202+
TNode<BoolT> ShouldRehash(TNode<IntPtrT> number_of_elements,
203+
TNode<IntPtrT> number_of_deleted);
204+
TNode<Word32T> ShouldShrink(TNode<IntPtrT> capacity,
205+
TNode<IntPtrT> number_of_elements);
206+
TNode<IntPtrT> ValueIndexFromKeyIndex(TNode<IntPtrT> key_index);
207+
};
208+
23209
} // namespace internal
24210
} // namespace v8
25211

‎deps/v8/src/builtins/builtins-weak-refs.cc

+21-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace v8 {
1010
namespace internal {
1111

12+
// https://tc39.es/proposal-symbols-as-weakmap-keys/#sec-finalization-registry.prototype.unregister
1213
BUILTIN(FinalizationRegistryUnregister) {
1314
HandleScope scope(isolate);
1415
const char* method_name = "FinalizationRegistry.prototype.unregister";
@@ -24,16 +25,29 @@ BUILTIN(FinalizationRegistryUnregister) {
2425

2526
Handle<Object> unregister_token = args.atOrUndefined(isolate, 1);
2627

27-
// 4. If Type(unregisterToken) is not Object, throw a TypeError exception.
28-
if (!unregister_token->IsJSReceiver()) {
29-
THROW_NEW_ERROR_RETURN_FAILURE(
30-
isolate,
31-
NewTypeError(MessageTemplate::kWeakRefsUnregisterTokenMustBeObject,
32-
unregister_token));
28+
// 4. If CanBeHeldWeakly(unregisterToken) is false, throw a TypeError
29+
// exception.
30+
if (FLAG_harmony_symbol_as_weakmap_key) {
31+
if (!unregister_token->IsJSReceiver() &&
32+
(!unregister_token->IsSymbol() ||
33+
Handle<Symbol>::cast(unregister_token)->is_in_public_symbol_table())) {
34+
THROW_NEW_ERROR_RETURN_FAILURE(
35+
isolate,
36+
NewTypeError(MessageTemplate::kInvalidWeakRefsUnregisterToken,
37+
unregister_token));
38+
}
39+
} else {
40+
// 4. If Type(unregisterToken) is not Object, throw a TypeError exception.
41+
if (!unregister_token->IsJSReceiver()) {
42+
THROW_NEW_ERROR_RETURN_FAILURE(
43+
isolate,
44+
NewTypeError(MessageTemplate::kInvalidWeakRefsUnregisterToken,
45+
unregister_token));
46+
}
3347
}
3448

3549
bool success = JSFinalizationRegistry::Unregister(
36-
finalization_registry, Handle<JSReceiver>::cast(unregister_token),
50+
finalization_registry, Handle<HeapObject>::cast(unregister_token),
3751
isolate);
3852

3953
return *isolate->factory()->ToBoolean(success);

‎deps/v8/src/builtins/cast.tq

+15
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,21 @@ Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null
697697
}
698698
}
699699

700+
Cast<JSReceiver|Symbol>(implicit context: Context)(o: Object): JSReceiver|Symbol
701+
labels CastError {
702+
typeswitch (o) {
703+
case (o: JSReceiver): {
704+
return o;
705+
}
706+
case (o: Symbol): {
707+
return o;
708+
}
709+
case (Object): {
710+
goto CastError;
711+
}
712+
}
713+
}
714+
700715
Cast<Smi|PromiseReaction>(o: Object): Smi|PromiseReaction labels CastError {
701716
typeswitch (o) {
702717
case (o: Smi): {

‎deps/v8/src/builtins/finalization-registry.tq

+21-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright 2020 the V8 project authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
4+
#include "src/builtins/builtins-collections-gen.h"
45

56
namespace runtime {
67
extern runtime
@@ -15,6 +16,9 @@ extern transitioning macro
1516
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
1617
JSFinalizationRegistry, WeakCell): void;
1718

19+
extern macro WeakCollectionsBuiltinsAssembler::GotoIfCannotBeWeakKey(JSAny):
20+
void labels NotWeakKey;
21+
1822
macro SplitOffTail(weakCell: WeakCell): WeakCell|Undefined {
1923
const weakCellTail = weakCell.next;
2024
weakCell.next = Undefined;
@@ -125,6 +129,7 @@ FinalizationRegistryConstructor(
125129
return finalizationRegistry;
126130
}
127131

132+
// https://tc39.es/proposal-symbols-as-weakmap-keys/#sec-finalization-registry.prototype.register
128133
transitioning javascript builtin
129134
FinalizationRegistryRegister(
130135
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
@@ -134,33 +139,32 @@ FinalizationRegistryRegister(
134139
ThrowTypeError(
135140
MessageTemplate::kIncompatibleMethodReceiver,
136141
'FinalizationRegistry.prototype.register', receiver);
137-
// 3. If Type(target) is not Object, throw a TypeError exception.
138-
const target = Cast<JSReceiver>(arguments[0]) otherwise ThrowTypeError(
139-
MessageTemplate::kWeakRefsRegisterTargetMustBeObject);
142+
// 3. If CanBeHeldWeakly(target) is false, throw a TypeError exception.
143+
GotoIfCannotBeWeakKey(arguments[0])
144+
otherwise ThrowTypeError(MessageTemplate::kInvalidWeakRefsRegisterTarget);
145+
146+
const target = UnsafeCast<(JSReceiver | Symbol)>(arguments[0]);
140147
const heldValue = arguments[1];
141148
// 4. If SameValue(target, heldValue), throw a TypeError exception.
142149
if (target == heldValue) {
143150
ThrowTypeError(
144151
MessageTemplate::kWeakRefsRegisterTargetAndHoldingsMustNotBeSame);
145152
}
146-
// 5. If Type(unregisterToken) is not Object,
153+
// 5. If CanBeHeldWeakly(unregisterToken) is false,
147154
// a. If unregisterToken is not undefined, throw a TypeError exception.
148155
// b. Set unregisterToken to empty.
149156
const unregisterTokenRaw = arguments[2];
150-
let unregisterToken: JSReceiver|Undefined;
151-
typeswitch (unregisterTokenRaw) {
152-
case (Undefined): {
153-
unregisterToken = Undefined;
154-
}
155-
case (unregisterTokenObj: JSReceiver): {
156-
unregisterToken = unregisterTokenObj;
157-
}
158-
case (JSAny): deferred {
159-
ThrowTypeError(
160-
MessageTemplate::kWeakRefsUnregisterTokenMustBeObject,
161-
unregisterTokenRaw);
162-
}
157+
let unregisterToken: JSReceiver|Undefined|Symbol;
158+
159+
if (IsUndefined(unregisterTokenRaw)) {
160+
unregisterToken = Undefined;
161+
} else {
162+
GotoIfCannotBeWeakKey(unregisterTokenRaw)
163+
otherwise ThrowTypeError(
164+
MessageTemplate::kInvalidWeakRefsUnregisterToken, unregisterTokenRaw);
165+
unregisterToken = UnsafeCast<(JSReceiver | Symbol)>(unregisterTokenRaw);
163166
}
167+
164168
// 6. Let cell be the Record { [[WeakRefTarget]] : target, [[HeldValue]]:
165169
// heldValue, [[UnregisterToken]]: unregisterToken }.
166170
// Allocate the WeakCell object in the old space, because 1) WeakCell weakness

‎deps/v8/src/builtins/weak-ref.tq

+13-7
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
#include 'src/builtins/builtins-collections-gen.h'
6+
57
namespace runtime {
68

7-
extern runtime JSWeakRefAddToKeptObjects(implicit context: Context)(JSReceiver):
8-
void;
9+
extern runtime JSWeakRefAddToKeptObjects(implicit context: Context)(
10+
JSReceiver | Symbol): void;
911

1012
} // namespace runtime
1113

1214
namespace weakref {
1315

16+
// https://tc39.es/proposal-symbols-as-weakmap-keys/#sec-weak-ref-target
1417
transitioning javascript builtin
1518
WeakRefConstructor(
1619
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
@@ -19,15 +22,17 @@ WeakRefConstructor(
1922
if (newTarget == Undefined) {
2023
ThrowTypeError(MessageTemplate::kConstructorNotFunction, 'WeakRef');
2124
}
22-
// 2. If Type(target) is not Object, throw a TypeError exception.
23-
const weakTarget = Cast<JSReceiver>(weakTarget) otherwise
24-
ThrowTypeError(
25-
MessageTemplate::kWeakRefsWeakRefConstructorTargetMustBeObject);
25+
26+
// 2. If CanBeHeldWeakly(weakTarget) is false, throw a TypeError exception.
27+
GotoIfCannotBeWeakKey(weakTarget) otherwise ThrowTypeError(
28+
MessageTemplate::kInvalidWeakRefsWeakRefConstructorTarget);
29+
2630
// 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget,
2731
// "%WeakRefPrototype%", « [[WeakRefTarget]] »).
2832
const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget));
2933
const weakRef = UnsafeCast<JSWeakRef>(AllocateFastOrSlowJSObjectFromMap(map));
3034
// 4. Perfom ! AddToKeptObjects(target).
35+
const weakTarget = UnsafeCast<(JSReceiver | Symbol)>(weakTarget);
3136
runtime::JSWeakRefAddToKeptObjects(weakTarget);
3237
// 5. Set weakRef.[[WeakRefTarget]] to target.
3338
weakRef.target = weakTarget;
@@ -52,7 +57,8 @@ WeakRefDeref(js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
5257
if (target != Undefined) {
5358
// JSWeakRefAddToKeptObjects might allocate and cause a GC, but it
5459
// won't clear `target` since we hold it here on the stack.
55-
runtime::JSWeakRefAddToKeptObjects(UnsafeCast<JSReceiver>(target));
60+
runtime::JSWeakRefAddToKeptObjects(
61+
UnsafeCast<(JSReceiver | Symbol)>(target));
5662
}
5763
return target;
5864
}

‎deps/v8/src/codegen/code-stub-assembler.h

+6
Original file line numberDiff line numberDiff line change
@@ -2679,6 +2679,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
26792679
ExternalReference::address_of_shared_string_table_flag());
26802680
}
26812681

2682+
TNode<BoolT> HasHarmonySymbolAsWeakmapKeyFlag() {
2683+
return LoadRuntimeFlag(
2684+
ExternalReference::
2685+
address_of_FLAG_harmony_symbol_as_weakmap_key());
2686+
}
2687+
26822688
// True iff |object| is a Smi or a HeapNumber or a BigInt.
26832689
TNode<BoolT> IsNumeric(TNode<Object> object);
26842690

‎deps/v8/src/codegen/external-reference.cc

+5
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,11 @@ ExternalReference::address_of_mock_arraybuffer_allocator_flag() {
550550
return ExternalReference(&FLAG_mock_arraybuffer_allocator);
551551
}
552552

553+
ExternalReference
554+
ExternalReference::address_of_FLAG_harmony_symbol_as_weakmap_key() {
555+
return ExternalReference(&FLAG_harmony_symbol_as_weakmap_key);
556+
}
557+
553558
ExternalReference ExternalReference::address_of_builtin_subclassing_flag() {
554559
return ExternalReference(&FLAG_builtin_subclassing);
555560
}

‎deps/v8/src/codegen/external-reference.h

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class StatsCounter;
9292

9393
#define EXTERNAL_REFERENCE_LIST(V) \
9494
V(abort_with_reason, "abort_with_reason") \
95+
V(address_of_FLAG_harmony_symbol_as_weakmap_key, \
96+
"FLAG_harmony_symbol_as_weakmap_key") \
9597
V(address_of_builtin_subclassing_flag, "FLAG_builtin_subclassing") \
9698
V(address_of_double_abs_constant, "double_absolute_constant") \
9799
V(address_of_double_neg_constant, "double_negate_constant") \

‎deps/v8/src/common/message-template.h

+4-6
Original file line numberDiff line numberDiff line change
@@ -633,17 +633,15 @@ namespace internal {
633633
T(TraceEventPhaseError, "Trace event phase must be a number.") \
634634
T(TraceEventIDError, "Trace event id must be a number.") \
635635
/* Weak refs */ \
636-
T(WeakRefsUnregisterTokenMustBeObject, \
637-
"unregisterToken ('%') must be an object") \
636+
T(InvalidWeakRefsUnregisterToken, "Invalid unregisterToken ('%')") \
638637
T(WeakRefsCleanupMustBeCallable, \
639638
"FinalizationRegistry: cleanup must be callable") \
640-
T(WeakRefsRegisterTargetMustBeObject, \
641-
"FinalizationRegistry.prototype.register: target must be an object") \
639+
T(InvalidWeakRefsRegisterTarget, \
640+
"FinalizationRegistry.prototype.register: invalid target") \
642641
T(WeakRefsRegisterTargetAndHoldingsMustNotBeSame, \
643642
"FinalizationRegistry.prototype.register: target and holdings must not " \
644643
"be same") \
645-
T(WeakRefsWeakRefConstructorTargetMustBeObject, \
646-
"WeakRef: target must be an object") \
644+
T(InvalidWeakRefsWeakRefConstructorTarget, "WeakRef: invalid target") \
647645
T(OptionalChainingNoNew, "Invalid optional chain from new expression") \
648646
T(OptionalChainingNoSuper, "Invalid optional chain from super property") \
649647
T(OptionalChainingNoTemplate, "Invalid tagged template on optional chain") \

‎deps/v8/src/diagnostics/objects-debug.cc

+6-2
Original file line numberDiff line numberDiff line change
@@ -1240,7 +1240,9 @@ void JSSharedStruct::JSSharedStructVerify(Isolate* isolate) {
12401240
void WeakCell::WeakCellVerify(Isolate* isolate) {
12411241
CHECK(IsWeakCell());
12421242

1243-
CHECK(target().IsJSReceiver() || target().IsUndefined(isolate));
1243+
CHECK(target().IsJSReceiver() || target().IsUndefined(isolate) ||
1244+
(target().IsSymbol() &&
1245+
!Symbol::cast(target()).is_in_public_symbol_table()));
12441246

12451247
CHECK(prev().IsWeakCell() || prev().IsUndefined(isolate));
12461248
if (prev().IsWeakCell()) {
@@ -1268,7 +1270,9 @@ void WeakCell::WeakCellVerify(Isolate* isolate) {
12681270
void JSWeakRef::JSWeakRefVerify(Isolate* isolate) {
12691271
CHECK(IsJSWeakRef());
12701272
JSObjectVerify(isolate);
1271-
CHECK(target().IsUndefined(isolate) || target().IsJSReceiver());
1273+
CHECK(target().IsUndefined(isolate) || target().IsJSReceiver() ||
1274+
(target().IsSymbol() &&
1275+
!Symbol::cast(target()).is_in_public_symbol_table()));
12721276
}
12731277

12741278
void JSFinalizationRegistry::JSFinalizationRegistryVerify(Isolate* isolate) {

‎deps/v8/src/flags/flag-definitions.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ DEFINE_BOOL(harmony_shipping, true, "enable all shipped harmony features")
307307
"harmony ResizableArrayBuffer / GrowableSharedArrayBuffer") \
308308
V(harmony_temporal, "Temporal") \
309309
V(harmony_shadow_realm, "harmony ShadowRealm") \
310-
V(harmony_struct, "harmony structs and shared structs")
310+
V(harmony_struct, "harmony structs and shared structs") \
311+
V(harmony_symbol_as_weakmap_key, "harmony symbols as weakmap keys")
311312

312313
#ifdef V8_INTL_SUPPORT
313314
#define HARMONY_INPROGRESS(V) \

‎deps/v8/src/heap/heap.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -6924,7 +6924,7 @@ void Heap::RemoveDirtyFinalizationRegistriesOnContext(NativeContext context) {
69246924
set_dirty_js_finalization_registries_list_tail(prev);
69256925
}
69266926

6927-
void Heap::KeepDuringJob(Handle<JSReceiver> target) {
6927+
void Heap::KeepDuringJob(Handle<HeapObject> target) {
69286928
DCHECK(weak_refs_keep_during_job().IsUndefined() ||
69296929
weak_refs_keep_during_job().IsOrderedHashSet());
69306930
Handle<OrderedHashSet> table;

‎deps/v8/src/heap/heap.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ class Heap {
955955
return is_finalization_registry_cleanup_task_posted_;
956956
}
957957

958-
V8_EXPORT_PRIVATE void KeepDuringJob(Handle<JSReceiver> target);
958+
V8_EXPORT_PRIVATE void KeepDuringJob(Handle<HeapObject> target);
959959
void ClearKeptObjects();
960960

961961
// ===========================================================================

‎deps/v8/src/init/bootstrapper.cc

+1
Original file line numberDiff line numberDiff line change
@@ -4484,6 +4484,7 @@ void Genesis::InitializeConsole(Handle<JSObject> extras_binding) {
44844484
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_assertions)
44854485
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_private_brand_checks)
44864486
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_static_blocks)
4487+
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_symbol_as_weakmap_key)
44874488
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_error_cause)
44884489

44894490
#ifdef V8_INTL_SUPPORT

‎deps/v8/src/objects/js-weak-refs-inl.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
#ifndef V8_OBJECTS_JS_WEAK_REFS_INL_H_
66
#define V8_OBJECTS_JS_WEAK_REFS_INL_H_
77

8-
#include "src/objects/js-weak-refs.h"
9-
108
#include "src/api/api-inl.h"
119
#include "src/heap/heap-write-barrier-inl.h"
10+
#include "src/objects/js-weak-refs.h"
1211
#include "src/objects/smi-inl.h"
1312

1413
// Has to be the last include (doesn't have include guards):
@@ -55,7 +54,7 @@ void JSFinalizationRegistry::RegisterWeakCellWithUnregisterToken(
5554

5655
bool JSFinalizationRegistry::Unregister(
5756
Handle<JSFinalizationRegistry> finalization_registry,
58-
Handle<JSReceiver> unregister_token, Isolate* isolate) {
57+
Handle<HeapObject> unregister_token, Isolate* isolate) {
5958
// Iterate through the doubly linked list of WeakCells associated with the
6059
// key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of
6160
// its FinalizationRegistry; remove it from there.
@@ -66,7 +65,7 @@ bool JSFinalizationRegistry::Unregister(
6665

6766
template <typename GCNotifyUpdatedSlotCallback>
6867
bool JSFinalizationRegistry::RemoveUnregisterToken(
69-
JSReceiver unregister_token, Isolate* isolate,
68+
HeapObject unregister_token, Isolate* isolate,
7069
RemoveUnregisterTokenMode removal_mode,
7170
GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
7271
// This method is called from both FinalizationRegistry#unregister and for
@@ -171,7 +170,9 @@ void WeakCell::Nullify(Isolate* isolate,
171170
// only called for WeakCells which haven't been unregistered yet, so they will
172171
// be in the active_cells list. (The caller must guard against calling this
173172
// for unregistered WeakCells by checking that the target is not undefined.)
174-
DCHECK(target().IsJSReceiver());
173+
DCHECK(target().IsJSReceiver() ||
174+
(target().IsSymbol() &&
175+
!Symbol::cast(target()).is_in_public_symbol_table()));
175176
set_target(ReadOnlyRoots(isolate).undefined_value());
176177

177178
JSFinalizationRegistry fr =

‎deps/v8/src/objects/js-weak-refs.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class JSFinalizationRegistry
3737
Handle<WeakCell> weak_cell, Isolate* isolate);
3838
inline static bool Unregister(
3939
Handle<JSFinalizationRegistry> finalization_registry,
40-
Handle<JSReceiver> unregister_token, Isolate* isolate);
40+
Handle<HeapObject> unregister_token, Isolate* isolate);
4141

4242
// RemoveUnregisterToken is called from both Unregister and during GC. Since
4343
// it modifies slots in key_map and WeakCells and the normal write barrier is
@@ -49,7 +49,7 @@ class JSFinalizationRegistry
4949
};
5050
template <typename GCNotifyUpdatedSlotCallback>
5151
inline bool RemoveUnregisterToken(
52-
JSReceiver unregister_token, Isolate* isolate,
52+
HeapObject unregister_token, Isolate* isolate,
5353
RemoveUnregisterTokenMode removal_mode,
5454
GCNotifyUpdatedSlotCallback gc_notify_updated_slot);
5555

‎deps/v8/src/objects/js-weak-refs.tq

+5-4
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ extern class JSFinalizationRegistry extends JSObject {
2020

2121
extern class WeakCell extends HeapObject {
2222
finalization_registry: Undefined|JSFinalizationRegistry;
23-
target: Undefined|JSReceiver;
24-
unregister_token: Undefined|JSReceiver;
23+
target: Undefined|JSReceiver|Symbol;
24+
unregister_token: Undefined|JSReceiver|Symbol;
2525
holdings: JSAny;
2626

2727
// For storing doubly linked lists of WeakCells in JSFinalizationRegistry's
@@ -38,5 +38,6 @@ extern class WeakCell extends HeapObject {
3838
key_list_prev: Undefined|WeakCell;
3939
key_list_next: Undefined|WeakCell;
4040
}
41-
42-
extern class JSWeakRef extends JSObject { target: Undefined|JSReceiver; }
41+
extern class JSWeakRef extends JSObject {
42+
target: Undefined|JSReceiver|Symbol;
43+
}

‎deps/v8/src/runtime/runtime-weak-refs.cc

+9-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
#include "src/runtime/runtime-utils.h"
6-
75
#include "src/execution/arguments-inl.h"
86
#include "src/objects/js-weak-refs-inl.h"
7+
#include "src/runtime/runtime-utils.h"
98

109
namespace v8 {
1110
namespace internal {
@@ -44,7 +43,14 @@ RUNTIME_FUNCTION(
4443
RUNTIME_FUNCTION(Runtime_JSWeakRefAddToKeptObjects) {
4544
HandleScope scope(isolate);
4645
DCHECK_EQ(1, args.length());
47-
Handle<JSReceiver> object = args.at<JSReceiver>(0);
46+
Handle<HeapObject> object = args.at<HeapObject>(0);
47+
if (FLAG_harmony_symbol_as_weakmap_key) {
48+
DCHECK(object->IsJSReceiver() ||
49+
(object->IsSymbol() &&
50+
!(Handle<Symbol>::cast(object))->is_in_public_symbol_table()));
51+
} else {
52+
DCHECK(object->IsJSReceiver());
53+
}
4854

4955
isolate->heap()->KeepDuringJob(object);
5056

Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
*%(basename)s:*: TypeError: FinalizationRegistry.prototype.register: target must be an object
1+
*%(basename)s:*: TypeError: FinalizationRegistry.prototype.register: invalid target
22
fg.register(1);
33
^
4-
TypeError: FinalizationRegistry.prototype.register: target must be an object
4+
TypeError: FinalizationRegistry.prototype.register: invalid target
55
at FinalizationRegistry.register (<anonymous>)
66
at *%(basename)s:*:4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
*%(basename)s:*: TypeError: unregisterToken ('1') must be an object
1+
*%(basename)s:*: TypeError: Invalid unregisterToken ('1')
22
fg.unregister(1);
33
^
4-
TypeError: unregisterToken ('1') must be an object
4+
TypeError: Invalid unregisterToken ('1')
55
at FinalizationRegistry.unregister (<anonymous>)
66
at *%(basename)s:*:4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2022 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flags: --harmony-symbol-as-weakmap-key --expose-gc
6+
7+
(function TestWeakMapWithNonRegisteredSymbolKey() {
8+
const key = Symbol('123');
9+
const value = 1;
10+
const map = new WeakMap();
11+
assertFalse(map.has(key));
12+
assertSame(undefined, map.get(key));
13+
assertFalse(map.delete(key));
14+
assertSame(map, map.set(key, value));
15+
assertSame(value, map.get(key));
16+
assertTrue(map.has(key));
17+
assertTrue(map.delete(key));
18+
assertFalse(map.has(key));
19+
assertSame(undefined, map.get(key));
20+
assertFalse(map.delete(key));
21+
assertFalse(map.has(key));
22+
assertSame(undefined, map.get(key));
23+
})();
24+
25+
(function TestWeakMapWithNonRegisteredSymbolKeyGC() {
26+
const map = new WeakMap();
27+
28+
const outerKey = Symbol('234');
29+
const outerValue = 1;
30+
map.set(outerKey, outerValue);
31+
{
32+
const innerKey = Symbol('123');
33+
const innerValue = 1;
34+
map.set(innerKey, innerValue);
35+
assertTrue(map.has(innerKey));
36+
assertSame(innerValue, map.get(innerKey));
37+
}
38+
gc();
39+
assertTrue(map.has(outerKey));
40+
assertSame(outerValue, map.get(outerKey));
41+
})();
42+
43+
(function TestWeakMapWithRegisteredSymbolKey() {
44+
const key = Symbol.for('123');
45+
const value = 1;
46+
const map = new WeakMap();
47+
assertFalse(map.has(key));
48+
assertSame(undefined, map.get(key));
49+
assertFalse(map.delete(key));
50+
assertThrows(() => {
51+
map.set(key, value);
52+
}, TypeError, 'Invalid value used as weak map key');
53+
assertFalse(map.has(key));
54+
assertSame(undefined, map.get(key));
55+
assertFalse(map.delete(key));
56+
assertFalse(map.has(key));
57+
assertSame(undefined, map.get(key));
58+
})();
59+
60+
(function TestWeakSetWithNonRegisteredSymbolKey() {
61+
const key = Symbol('123');
62+
const set = new WeakSet();
63+
assertFalse(set.has(key));
64+
assertFalse(set.delete(key));
65+
assertSame(set, set.add(key));
66+
assertTrue(set.has(key));
67+
assertTrue(set.delete(key));
68+
assertFalse(set.has(key));
69+
assertFalse(set.delete(key));
70+
assertFalse(set.has(key));
71+
})();
72+
73+
(function TestWeakSetWithNonRegisteredSymbolKeyGC() {
74+
const set = new WeakSet();
75+
const outerKey = Symbol('234');
76+
set.add(outerKey);
77+
{
78+
const innerKey = Symbol('123');
79+
set.add(innerKey);
80+
assertTrue(set.has(innerKey));
81+
}
82+
gc();
83+
assertTrue(set.has(outerKey));
84+
})();
85+
86+
(function TestWeakSetWithRegisteredSymbolKey() {
87+
const key = Symbol.for('123');
88+
const set = new WeakSet();
89+
assertFalse(set.has(key));
90+
assertFalse(set.delete(key));
91+
92+
assertThrows(() => {
93+
assertSame(set, set.add(key));
94+
}, TypeError, 'Invalid value used in weak set');
95+
96+
assertFalse(set.has(key));
97+
assertFalse(set.delete(key));
98+
assertFalse(set.has(key));
99+
})();

‎deps/v8/test/mjsunit/harmony/weakrefs/basics.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
(function TestRegisterWithNonObjectTarget() {
4949
let fg = new FinalizationRegistry(() => {});
50-
let message = "FinalizationRegistry.prototype.register: target must be an object";
50+
let message = "FinalizationRegistry.prototype.register: invalid target";
5151
assertThrows(() => fg.register(1, "holdings"), TypeError, message);
5252
assertThrows(() => fg.register(false, "holdings"), TypeError, message);
5353
assertThrows(() => fg.register("foo", "holdings"), TypeError, message);
@@ -116,7 +116,7 @@
116116
})();
117117

118118
(function TestWeakRefConstructorWithNonObject() {
119-
let message = "WeakRef: target must be an object";
119+
let message = "WeakRef: invalid target";
120120
assertThrows(() => new WeakRef(), TypeError, message);
121121
assertThrows(() => new WeakRef(1), TypeError, message);
122122
assertThrows(() => new WeakRef(false), TypeError, message);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2022 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flags: --harmony-symbol-as-weakmap-key --expose-gc --noincremental-marking
6+
7+
(function TestWeakRefWithSymbolGC() {
8+
let weakRef;
9+
{
10+
const innerKey = Symbol('123');
11+
weakRef = new WeakRef(innerKey);
12+
}
13+
// Since the WeakRef was created during this turn, it is not cleared by GC.
14+
gc();
15+
assertNotEquals(undefined, weakRef.deref());
16+
// Next task.
17+
setTimeout(() => {
18+
gc();
19+
assertEquals(undefined, weakRef.deref());
20+
}, 0);
21+
})();
22+
23+
(function TestFinalizationRegistryWithSymbolGC() {
24+
let cleanUpCalled = false;
25+
const fg = new FinalizationRegistry((target) => {
26+
assertEquals('123', target);
27+
cleanUpCalled = true;
28+
});
29+
(function () {
30+
const innerKey = Symbol('123');
31+
fg.register(innerKey, '123');
32+
})();
33+
gc();
34+
assertFalse(cleanUpCalled);
35+
// Check that cleanup callback was called in a follow up task.
36+
setTimeout(() => {
37+
assertTrue(cleanUpCalled);
38+
}, 0);
39+
})();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2022 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flags: --harmony-symbol-as-weakmap-key
6+
7+
(function TestRegisterWithSymbolTarget() {
8+
const fg = new FinalizationRegistry(() => { });
9+
fg.register(Symbol('123'), 'holdings');
10+
// Registered symbols cannot be the target.
11+
assertThrows(() => fg.register(Symbol.for('123'), 'holdings'), TypeError);
12+
})();
13+
14+
(function TestRegisterWithSymbolUnregisterToken() {
15+
const fg = new FinalizationRegistry(() => { });
16+
fg.register({}, 'holdings', Symbol('123'));
17+
// Registered symbols cannot be the unregister token.
18+
assertThrows(() => fg.register({}, 'holdings', Symbol.for('123')), TypeError);
19+
})();
20+
21+
(function TestRegisterSymbolAndHoldingsSameValue() {
22+
const fg = new FinalizationRegistry(() => {});
23+
const obj = Symbol('123');
24+
// SameValue(target, holdings) not ok.
25+
assertThrows(() => fg.register(obj, obj), TypeError);
26+
const holdings = {a: 1};
27+
fg.register(obj, holdings);
28+
})();
29+
30+
(function TestUnregisterWithSymbolUnregisterToken() {
31+
const fg = new FinalizationRegistry(() => {});
32+
fg.unregister(Symbol('123'));
33+
// Registered symbols cannot be the unregister token.
34+
assertThrows(() => fg.unregister(Symbol.for('123')), TypeError);
35+
})();
36+
37+
(function TestWeakRefConstructorWithSymbol() {
38+
new WeakRef(Symbol('123'));
39+
// Registered symbols cannot be the WeakRef target.
40+
assertThrows(() => new WeakRef(Symbol.for('123')), TypeError);
41+
})();

0 commit comments

Comments
 (0)
Please sign in to comment.