Skip to content

Commit c400af4

Browse files
jameslahmV8 LUCI CQ
authored andcommitted
[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 <[email protected]> Commit-Queue: Marja Hölttä <[email protected]> Reviewed-by: Jakob Linke <[email protected]> Cr-Commit-Position: refs/heads/main@{#83313}
1 parent 4ad7702 commit c400af4

26 files changed

Lines changed: 533 additions & 260 deletions

src/builtins/base.tq

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,9 @@ extern enum MessageTemplate {
439439
kWasmTrapStringOffsetOutOfBounds,
440440
kWasmObjectsAreOpaque,
441441
kWeakRefsRegisterTargetAndHoldingsMustNotBeSame,
442-
kWeakRefsRegisterTargetMustBeObject,
443-
kWeakRefsUnregisterTokenMustBeObject,
444-
kWeakRefsWeakRefConstructorTargetMustBeObject,
442+
kInvalidWeakRefsRegisterTarget,
443+
kInvalidWeakRefsUnregisterToken,
444+
kInvalidWeakRefsWeakRefConstructorTarget,
445445
...
446446
}
447447

@@ -918,10 +918,10 @@ macro Float64IsNaN(n: float64): bool {
918918
// The type of all tagged values that can safely be compared with TaggedEqual.
919919
@if(V8_ENABLE_WEBASSEMBLY)
920920
type TaggedWithIdentity = JSReceiver | FixedArrayBase | Oddball | Map |
921-
WeakCell | Context | EmptyString | WasmInternalFunction;
921+
WeakCell | Context | EmptyString | Symbol | WasmInternalFunction;
922922
@ifnot(V8_ENABLE_WEBASSEMBLY)
923923
type TaggedWithIdentity = JSReceiver | FixedArrayBase | Oddball | Map |
924-
WeakCell | Context | EmptyString;
924+
WeakCell | Context | EmptyString | Symbol;
925925

926926
extern operator '==' macro TaggedEqual(TaggedWithIdentity, Object): bool;
927927
extern operator '==' macro TaggedEqual(Object, TaggedWithIdentity): bool;

src/builtins/builtins-collections-gen.cc

Lines changed: 38 additions & 193 deletions
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,
@@ -523,16 +399,28 @@ TNode<IntPtrT> BaseCollectionsAssembler::EstimatedInitialSize(
523399
[=] { return IntPtrConstant(0); });
524400
}
525401

402+
// https://tc39.es/proposal-symbols-as-weakmap-keys/#sec-canbeheldweakly-abstract-operation
526403
void BaseCollectionsAssembler::GotoIfCannotBeWeakKey(
527404
const TNode<Object> obj, Label* if_cannot_be_weak_key) {
405+
Label check_symbol_key(this);
406+
Label end(this);
528407
GotoIf(TaggedIsSmi(obj), if_cannot_be_weak_key);
529408
TNode<Uint16T> instance_type = LoadMapInstanceType(LoadMap(CAST(obj)));
530-
GotoIfNot(IsJSReceiverInstanceType(instance_type), if_cannot_be_weak_key);
409+
GotoIfNot(IsJSReceiverInstanceType(instance_type), &check_symbol_key);
531410
// TODO(v8:12547) Shared structs and arrays should only be able to point
532411
// to shared values in weak collections. For now, disallow them as weak
533412
// collection keys.
534413
GotoIf(IsJSSharedStructInstanceType(instance_type), if_cannot_be_weak_key);
535414
GotoIf(IsJSSharedArrayInstanceType(instance_type), if_cannot_be_weak_key);
415+
Goto(&end);
416+
Bind(&check_symbol_key);
417+
GotoIfNot(HasHarmonySymbolAsWeakmapKeyFlag(), if_cannot_be_weak_key);
418+
GotoIfNot(IsSymbolInstanceType(instance_type), if_cannot_be_weak_key);
419+
TNode<Uint32T> flags = LoadSymbolFlags(CAST(obj));
420+
GotoIf(Word32And(flags, Symbol::IsInPublicSymbolTableBit::kMask),
421+
if_cannot_be_weak_key);
422+
Goto(&end);
423+
Bind(&end);
536424
}
537425

538426
TNode<Map> BaseCollectionsAssembler::GetInitialCollectionPrototype(
@@ -2414,67 +2302,6 @@ TF_BUILTIN(FindOrderedHashSetEntry, CollectionsBuiltinsAssembler) {
24142302
Return(SmiConstant(-1));
24152303
}
24162304

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

2320+
TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::GetHash(
2321+
const TNode<HeapObject> key, Label* if_no_hash) {
2322+
TVARIABLE(IntPtrT, var_hash);
2323+
Label if_symbol(this);
2324+
Label return_result(this);
2325+
GotoIfNot(IsJSReceiver(key), &if_symbol);
2326+
var_hash = LoadJSReceiverIdentityHash(CAST(key), if_no_hash);
2327+
Goto(&return_result);
2328+
Bind(&if_symbol);
2329+
CSA_DCHECK(this, IsSymbol(key));
2330+
CSA_DCHECK(this, Word32BinaryNot(
2331+
Word32And(LoadSymbolFlags(CAST(key)),
2332+
Symbol::IsInPublicSymbolTableBit::kMask)));
2333+
var_hash = ChangeInt32ToIntPtr(LoadNameHash(CAST(key), nullptr));
2334+
Goto(&return_result);
2335+
Bind(&return_result);
2336+
return var_hash.value();
2337+
}
2338+
24932339
TNode<HeapObject> WeakCollectionsBuiltinsAssembler::AllocateTable(
24942340
Variant variant, TNode<IntPtrT> at_least_space_for) {
24952341
// See HashTable::New().
@@ -2719,8 +2565,7 @@ TF_BUILTIN(WeakMapLookupHashIndex, WeakCollectionsBuiltinsAssembler) {
27192565

27202566
GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);
27212567

2722-
TNode<IntPtrT> hash =
2723-
LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
2568+
TNode<IntPtrT> hash = GetHash(CAST(key), &if_cannot_be_weak_key);
27242569
TNode<IntPtrT> capacity = LoadTableCapacity(table);
27252570
TNode<IntPtrT> key_index = FindKeyIndexForKey(
27262571
table, key, hash, EntryMask(capacity), &if_cannot_be_weak_key);
@@ -2785,8 +2630,7 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {
27852630

27862631
GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);
27872632

2788-
TNode<IntPtrT> hash =
2789-
LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
2633+
TNode<IntPtrT> hash = GetHash(CAST(key), &if_cannot_be_weak_key);
27902634
TNode<EphemeronHashTable> table = LoadTable(collection);
27912635
TNode<IntPtrT> capacity = LoadTableCapacity(table);
27922636
TNode<IntPtrT> key_index = FindKeyIndexForKey(
@@ -2810,18 +2654,18 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {
28102654
TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) {
28112655
auto context = Parameter<Context>(Descriptor::kContext);
28122656
auto collection = Parameter<JSWeakCollection>(Descriptor::kCollection);
2813-
auto key = Parameter<JSReceiver>(Descriptor::kKey);
2657+
auto key = Parameter<HeapObject>(Descriptor::kKey);
28142658
auto value = Parameter<Object>(Descriptor::kValue);
28152659

2816-
CSA_DCHECK(this, IsJSReceiver(key));
2660+
CSA_DCHECK(this, Word32Or(IsJSReceiver(key), IsSymbol(key)));
28172661

28182662
Label call_runtime(this), if_no_hash(this), if_not_found(this);
28192663

28202664
TNode<EphemeronHashTable> table = LoadTable(collection);
28212665
TNode<IntPtrT> capacity = LoadTableCapacity(table);
28222666
TNode<IntPtrT> entry_mask = EntryMask(capacity);
28232667

2824-
TVARIABLE(IntPtrT, var_hash, LoadJSReceiverIdentityHash(key, &if_no_hash));
2668+
TVARIABLE(IntPtrT, var_hash, GetHash(key, &if_no_hash));
28252669
TNode<IntPtrT> key_index = FindKeyIndexForKey(table, key, var_hash.value(),
28262670
entry_mask, &if_not_found);
28272671

@@ -2830,6 +2674,7 @@ TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) {
28302674

28312675
BIND(&if_no_hash);
28322676
{
2677+
CSA_DCHECK(this, IsJSReceiver(key));
28332678
var_hash = SmiUntag(CreateIdentityHash(key));
28342679
Goto(&if_not_found);
28352680
}

0 commit comments

Comments
 (0)