Skip to content

Commit

Permalink
deps: V8: cherry-pick c400af48b5ef
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
joyeecheung authored and richardlau committed Mar 15, 2024
1 parent 902d8b3 commit 1ce901b
Show file tree
Hide file tree
Showing 27 changed files with 534 additions and 261 deletions.
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -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 #####

Expand Down
10 changes: 5 additions & 5 deletions deps/v8/src/builtins/base.tq
Original file line number Diff line number Diff line change
Expand Up @@ -438,9 +438,9 @@ extern enum MessageTemplate {
kWasmTrapArrayOutOfBounds,
kWasmTrapArrayTooLarge,
kWeakRefsRegisterTargetAndHoldingsMustNotBeSame,
kWeakRefsRegisterTargetMustBeObject,
kWeakRefsUnregisterTokenMustBeObject,
kWeakRefsWeakRefConstructorTargetMustBeObject,
kInvalidWeakRefsRegisterTarget,
kInvalidWeakRefsUnregisterToken,
kInvalidWeakRefsWeakRefConstructorTarget,
...
}

Expand Down Expand Up @@ -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;
Expand Down
230 changes: 37 additions & 193 deletions deps/v8/src/builtins/builtins-collections-gen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,130 +22,6 @@ namespace internal {
template <class T>
using TVariable = compiler::TypedCodeAssemblerVariable<T>;

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> context,
TNode<Object> collection, TNode<Object> add_function,
TNode<Object> key_value,
Label* if_may_have_side_effects = nullptr,
Label* if_exception = nullptr,
TVariable<Object>* var_exception = nullptr);

// Adds constructor entries to a collection. Choosing a fast path when
// possible.
void AddConstructorEntries(Variant variant, TNode<Context> context,
TNode<Context> native_context,
TNode<HeapObject> collection,
TNode<Object> 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> context,
TNode<Context> native_context,
TNode<Object> collection,
TNode<JSArray> fast_jsarray,
Label* if_may_have_side_effects);

// Adds constructor entries to a collection using the iterator protocol.
void AddConstructorEntriesFromIterable(Variant variant,
TNode<Context> context,
TNode<Context> native_context,
TNode<Object> collection,
TNode<Object> iterable);

// Constructs a collection instance. Choosing a fast path when possible.
TNode<JSObject> AllocateJSCollection(TNode<Context> context,
TNode<JSFunction> constructor,
TNode<JSReceiver> new_target);

// Fast path for constructing a collection instance if the constructor
// function has not been modified.
TNode<JSObject> AllocateJSCollectionFast(TNode<JSFunction> constructor);

// Fallback for constructing a collection instance if the constructor function
// has been modified.
TNode<JSObject> AllocateJSCollectionSlow(TNode<Context> context,
TNode<JSFunction> constructor,
TNode<JSReceiver> new_target);

// Allocates the backing store for a collection.
virtual TNode<HeapObject> AllocateTable(
Variant variant, TNode<IntPtrT> at_least_space_for) = 0;

// Main entry point for a collection constructor builtin.
void GenerateConstructor(Variant variant,
Handle<String> constructor_function_name,
TNode<Object> new_target, TNode<IntPtrT> argc,
TNode<Context> context);

// Retrieves the collection function that adds an entry. `set` for Maps and
// `add` for Sets.
TNode<Object> GetAddFunction(Variant variant, TNode<Context> context,
TNode<Object> collection);

// Retrieves the collection constructor function.
TNode<JSFunction> GetConstructor(Variant variant,
TNode<Context> 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<JSFunction> GetInitialAddFunction(Variant variant,
TNode<Context> 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<NativeContext> native_context,
TNode<HeapObject> 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<IntPtrT> EstimatedInitialSize(TNode<Object> initial_entries,
TNode<BoolT> is_fast_jsarray);

void GotoIfCannotBeWeakKey(const TNode<Object> obj,
Label* if_cannot_be_weak_key);

// Determines whether the collection's prototype has been modified.
TNode<BoolT> HasInitialCollectionPrototype(Variant variant,
TNode<Context> native_context,
TNode<Object> collection);

// Gets the initial prototype map for given collection {variant}.
TNode<Map> GetInitialCollectionPrototype(Variant variant,
TNode<Context> native_context);

// Loads an element from a fixed array. If the element is the hole, returns
// `undefined`.
TNode<Object> LoadAndNormalizeFixedArrayElement(TNode<FixedArray> elements,
TNode<IntPtrT> index);

// Loads an element from a fixed double array. If the element is the hole,
// returns `undefined`.
TNode<Object> LoadAndNormalizeFixedDoubleArrayElement(
TNode<HeapObject> elements, TNode<IntPtrT> index);
};

void BaseCollectionsAssembler::AddConstructorEntry(
Variant variant, TNode<Context> context, TNode<Object> collection,
TNode<Object> add_function, TNode<Object> key_value,
Expand Down Expand Up @@ -525,12 +401,23 @@ TNode<IntPtrT> BaseCollectionsAssembler::EstimatedInitialSize(

void BaseCollectionsAssembler::GotoIfCannotBeWeakKey(
const TNode<Object> obj, Label* if_cannot_be_weak_key) {
Label check_symbol_key(this);
Label end(this);
GotoIf(TaggedIsSmi(obj), if_cannot_be_weak_key);
TNode<Uint16T> instance_type = LoadMapInstanceType(LoadMap(CAST(obj)));
GotoIfNot(IsJSReceiverInstanceType(instance_type), if_cannot_be_weak_key);
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<Uint32T> flags = LoadSymbolFlags(CAST(obj));
GotoIf(Word32And(flags, Symbol::IsInPublicSymbolTableBit::kMask),
if_cannot_be_weak_key);
Goto(&end);
Bind(&end);
}

TNode<Map> BaseCollectionsAssembler::GetInitialCollectionPrototype(
Expand Down Expand Up @@ -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<EphemeronHashTable> table, TNode<IntPtrT> key_index,
TNode<Object> key, TNode<Object> value,
TNode<IntPtrT> number_of_elements);

TNode<HeapObject> AllocateTable(Variant variant,
TNode<IntPtrT> at_least_space_for) override;

// Generates and sets the identity for a JSRececiver.
TNode<Smi> CreateIdentityHash(TNode<Object> receiver);
TNode<IntPtrT> EntryMask(TNode<IntPtrT> 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<void(TNode<Object> entry_key, Label* if_same)>;
TNode<IntPtrT> FindKeyIndex(TNode<HeapObject> table, TNode<IntPtrT> key_hash,
TNode<IntPtrT> entry_mask,
const KeyComparator& key_compare);

// Builds code that finds an EphemeronHashTable entry available for a new
// entry.
TNode<IntPtrT> FindKeyIndexForInsertion(TNode<HeapObject> table,
TNode<IntPtrT> key_hash,
TNode<IntPtrT> 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<IntPtrT> FindKeyIndexForKey(TNode<HeapObject> table, TNode<Object> key,
TNode<IntPtrT> hash,
TNode<IntPtrT> entry_mask,
Label* if_not_found);

TNode<Word32T> InsufficientCapacityToAdd(TNode<IntPtrT> capacity,
TNode<IntPtrT> number_of_elements,
TNode<IntPtrT> number_of_deleted);
TNode<IntPtrT> KeyIndexFromEntry(TNode<IntPtrT> entry);

TNode<IntPtrT> LoadNumberOfElements(TNode<EphemeronHashTable> table,
int offset);
TNode<IntPtrT> LoadNumberOfDeleted(TNode<EphemeronHashTable> table,
int offset = 0);
TNode<EphemeronHashTable> LoadTable(TNode<JSWeakCollection> collection);
TNode<IntPtrT> LoadTableCapacity(TNode<EphemeronHashTable> table);

void RemoveEntry(TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
TNode<IntPtrT> number_of_elements);
TNode<BoolT> ShouldRehash(TNode<IntPtrT> number_of_elements,
TNode<IntPtrT> number_of_deleted);
TNode<Word32T> ShouldShrink(TNode<IntPtrT> capacity,
TNode<IntPtrT> number_of_elements);
TNode<IntPtrT> ValueIndexFromKeyIndex(TNode<IntPtrT> key_index);
};

void WeakCollectionsBuiltinsAssembler::AddEntry(
TNode<EphemeronHashTable> table, TNode<IntPtrT> key_index,
TNode<Object> key, TNode<Object> value, TNode<IntPtrT> number_of_elements) {
Expand All @@ -2503,6 +2329,25 @@ void WeakCollectionsBuiltinsAssembler::AddEntry(
SmiFromIntPtr(number_of_elements));
}

TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::GetHash(
const TNode<HeapObject> 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<HeapObject> WeakCollectionsBuiltinsAssembler::AllocateTable(
Variant variant, TNode<IntPtrT> at_least_space_for) {
// See HashTable::New().
Expand Down Expand Up @@ -2732,8 +2577,7 @@ TF_BUILTIN(WeakMapLookupHashIndex, WeakCollectionsBuiltinsAssembler) {

GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);

TNode<IntPtrT> hash =
LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
TNode<IntPtrT> hash = GetHash(CAST(key), &if_cannot_be_weak_key);
TNode<IntPtrT> capacity = LoadTableCapacity(table);
TNode<IntPtrT> key_index = FindKeyIndexForKey(
table, key, hash, EntryMask(capacity), &if_cannot_be_weak_key);
Expand Down Expand Up @@ -2798,8 +2642,7 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {

GotoIfCannotBeWeakKey(key, &if_cannot_be_weak_key);

TNode<IntPtrT> hash =
LoadJSReceiverIdentityHash(CAST(key), &if_cannot_be_weak_key);
TNode<IntPtrT> hash = GetHash(CAST(key), &if_cannot_be_weak_key);
TNode<EphemeronHashTable> table = LoadTable(collection);
TNode<IntPtrT> capacity = LoadTableCapacity(table);
TNode<IntPtrT> key_index = FindKeyIndexForKey(
Expand All @@ -2823,18 +2666,18 @@ TF_BUILTIN(WeakCollectionDelete, WeakCollectionsBuiltinsAssembler) {
TF_BUILTIN(WeakCollectionSet, WeakCollectionsBuiltinsAssembler) {
auto context = Parameter<Context>(Descriptor::kContext);
auto collection = Parameter<JSWeakCollection>(Descriptor::kCollection);
auto key = Parameter<JSReceiver>(Descriptor::kKey);
auto key = Parameter<HeapObject>(Descriptor::kKey);
auto value = Parameter<Object>(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);

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

TVARIABLE(IntPtrT, var_hash, LoadJSReceiverIdentityHash(key, &if_no_hash));
TVARIABLE(IntPtrT, var_hash, GetHash(key, &if_no_hash));
TNode<IntPtrT> key_index = FindKeyIndexForKey(table, key, var_hash.value(),
entry_mask, &if_not_found);

Expand All @@ -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);
}
Expand Down

0 comments on commit 1ce901b

Please sign in to comment.