mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
deps: V8: backport 22014de00115
Original commit message:
Reland "[snapshot] rehash JSMap and JSSet during deserialization"
This is a reland of 8374feed55a5b3010f2e9593560a2d84f9f6725f.
Fixed rehashing of global proxy keys by creating its identity hash
early, before the deserialization of the context snapshot.
Original change's description:
> [snapshot] rehash JSMap and JSSet during deserialization
>
> To rehash JSMap and JSSet, we simply replace the backing store
> with a new one created with the new hash.
>
> Bug: v8:9187
> Change-Id: I90c25b18b33b7bc2b6ffe1b89fe17aa5f978b517
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2143983
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Camillo Bruni <cbruni@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#67663}
Bug: v8:9187, v8:10523
Change-Id: I7a0319b1d10ff07644de902fec43e7c2b1dd8da9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2212085
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/master@{#67999}
Refs: https://github.com/v8/v8/commit/22014de00115dae09ae3d4a6c3a9f178d5495ef2
PR-URL: https://github.com/nodejs/node/pull/33300
Refs: https://github.com/v8/v8/commit/ea0719b8ed087d1f511e78595dcb596faa7638d0
Refs: https://github.com/v8/v8/commit/bb9f0c2b2fe920a717794f3279758846f59f7840
Refs: https://github.com/nodejs/node/issues/17058
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
12 files changed
+104
-16
No files matched your search
+1
-1
@@ -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.16',
|
||||
'v8_embedder_string': '-node.17',
|
||||
|
||||
##### V8 defaults for Node.js #####
|
||||
|
||||
|
||||
Vendored
+5
-1
@@ -2761,8 +2761,12 @@ Handle<JSGlobalProxy> Factory::NewUninitializedJSGlobalProxy(int size) {
|
||||
map->set_is_access_check_needed(true);
|
||||
map->set_may_have_interesting_symbols(true);
|
||||
LOG(isolate(), MapDetails(*map));
|
||||
return Handle<JSGlobalProxy>::cast(
|
||||
Handle<JSGlobalProxy> proxy = Handle<JSGlobalProxy>::cast(
|
||||
NewJSObjectFromMap(map, AllocationType::kYoung));
|
||||
// Create identity hash early in case there is any JS collection containing
|
||||
// a global proxy key and needs to be rehashed after deserialization.
|
||||
proxy->GetOrCreateIdentityHash(isolate());
|
||||
return proxy;
|
||||
}
|
||||
|
||||
void Factory::ReinitializeJSGlobalProxy(Handle<JSGlobalProxy> object,
|
||||
|
||||
Vendored
+1
-1
@@ -191,7 +191,7 @@ class HeapObject : public Object {
|
||||
bool CanBeRehashed() const;
|
||||
|
||||
// Rehash the object based on the layout inferred from its map.
|
||||
void RehashBasedOnMap(ReadOnlyRoots root);
|
||||
void RehashBasedOnMap(Isolate* isolate);
|
||||
|
||||
// Layout description.
|
||||
#define HEAP_OBJECT_FIELDS(V) \
|
||||
|
||||
Vendored
+2
@@ -30,6 +30,7 @@ class JSSet : public TorqueGeneratedJSSet<JSSet, JSCollection> {
|
||||
public:
|
||||
static void Initialize(Handle<JSSet> set, Isolate* isolate);
|
||||
static void Clear(Isolate* isolate, Handle<JSSet> set);
|
||||
void Rehash(Isolate* isolate);
|
||||
|
||||
// Dispatched behavior.
|
||||
DECL_PRINTER(JSSet)
|
||||
@@ -56,6 +57,7 @@ class JSMap : public TorqueGeneratedJSMap<JSMap, JSCollection> {
|
||||
public:
|
||||
static void Initialize(Handle<JSMap> map, Isolate* isolate);
|
||||
static void Clear(Isolate* isolate, Handle<JSMap> map);
|
||||
void Rehash(Isolate* isolate);
|
||||
|
||||
// Dispatched behavior.
|
||||
DECL_PRINTER(JSMap)
|
||||
|
||||
Vendored
+34
-4
@@ -2305,9 +2305,8 @@ bool HeapObject::NeedsRehashing() const {
|
||||
case TRANSITION_ARRAY_TYPE:
|
||||
return TransitionArray::cast(*this).number_of_entries() > 1;
|
||||
case ORDERED_HASH_MAP_TYPE:
|
||||
return OrderedHashMap::cast(*this).NumberOfElements() > 0;
|
||||
case ORDERED_HASH_SET_TYPE:
|
||||
return OrderedHashSet::cast(*this).NumberOfElements() > 0;
|
||||
return false; // We'll rehash from the JSMap or JSSet referencing them.
|
||||
case NAME_DICTIONARY_TYPE:
|
||||
case GLOBAL_DICTIONARY_TYPE:
|
||||
case NUMBER_DICTIONARY_TYPE:
|
||||
@@ -2317,6 +2316,8 @@ bool HeapObject::NeedsRehashing() const {
|
||||
case SMALL_ORDERED_HASH_MAP_TYPE:
|
||||
case SMALL_ORDERED_HASH_SET_TYPE:
|
||||
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
|
||||
case JS_MAP_TYPE:
|
||||
case JS_SET_TYPE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -2326,10 +2327,13 @@ bool HeapObject::NeedsRehashing() const {
|
||||
bool HeapObject::CanBeRehashed() const {
|
||||
DCHECK(NeedsRehashing());
|
||||
switch (map().instance_type()) {
|
||||
case JS_MAP_TYPE:
|
||||
case JS_SET_TYPE:
|
||||
return true;
|
||||
case ORDERED_HASH_MAP_TYPE:
|
||||
case ORDERED_HASH_SET_TYPE:
|
||||
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
|
||||
case ORDERED_NAME_DICTIONARY_TYPE:
|
||||
// TODO(yangguo): actually support rehashing OrderedHash{Map,Set}.
|
||||
return false;
|
||||
case NAME_DICTIONARY_TYPE:
|
||||
case GLOBAL_DICTIONARY_TYPE:
|
||||
@@ -2353,7 +2357,8 @@ bool HeapObject::CanBeRehashed() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) {
|
||||
void HeapObject::RehashBasedOnMap(Isolate* isolate) {
|
||||
ReadOnlyRoots roots = ReadOnlyRoots(isolate);
|
||||
switch (map().instance_type()) {
|
||||
case HASH_TABLE_TYPE:
|
||||
UNREACHABLE();
|
||||
@@ -2385,6 +2390,17 @@ void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) {
|
||||
case SMALL_ORDERED_HASH_SET_TYPE:
|
||||
DCHECK_EQ(0, SmallOrderedHashSet::cast(*this).NumberOfElements());
|
||||
break;
|
||||
case ORDERED_HASH_MAP_TYPE:
|
||||
case ORDERED_HASH_SET_TYPE:
|
||||
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
|
||||
case JS_MAP_TYPE: {
|
||||
JSMap::cast(*this).Rehash(isolate);
|
||||
break;
|
||||
}
|
||||
case JS_SET_TYPE: {
|
||||
JSSet::cast(*this).Rehash(isolate);
|
||||
break;
|
||||
}
|
||||
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
|
||||
DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements());
|
||||
break;
|
||||
@@ -7864,6 +7880,13 @@ void JSSet::Clear(Isolate* isolate, Handle<JSSet> set) {
|
||||
set->set_table(*table);
|
||||
}
|
||||
|
||||
void JSSet::Rehash(Isolate* isolate) {
|
||||
Handle<OrderedHashSet> table_handle(OrderedHashSet::cast(table()), isolate);
|
||||
Handle<OrderedHashSet> new_table =
|
||||
OrderedHashSet::Rehash(isolate, table_handle).ToHandleChecked();
|
||||
set_table(*new_table);
|
||||
}
|
||||
|
||||
void JSMap::Initialize(Handle<JSMap> map, Isolate* isolate) {
|
||||
Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
|
||||
map->set_table(*table);
|
||||
@@ -7875,6 +7898,13 @@ void JSMap::Clear(Isolate* isolate, Handle<JSMap> map) {
|
||||
map->set_table(*table);
|
||||
}
|
||||
|
||||
void JSMap::Rehash(Isolate* isolate) {
|
||||
Handle<OrderedHashMap> table_handle(OrderedHashMap::cast(table()), isolate);
|
||||
Handle<OrderedHashMap> new_table =
|
||||
OrderedHashMap::Rehash(isolate, table_handle).ToHandleChecked();
|
||||
set_table(*new_table);
|
||||
}
|
||||
|
||||
void JSWeakCollection::Initialize(Handle<JSWeakCollection> weak_collection,
|
||||
Isolate* isolate) {
|
||||
Handle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0);
|
||||
|
||||
+21
@@ -194,6 +194,13 @@ HeapObject OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) {
|
||||
return ro_roots.empty_ordered_hash_map();
|
||||
}
|
||||
|
||||
template <class Derived, int entrysize>
|
||||
MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
|
||||
Isolate* isolate, Handle<Derived> table) {
|
||||
return OrderedHashTable<Derived, entrysize>::Rehash(isolate, table,
|
||||
table->Capacity());
|
||||
}
|
||||
|
||||
template <class Derived, int entrysize>
|
||||
MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
|
||||
Isolate* isolate, Handle<Derived> table, int new_capacity) {
|
||||
@@ -250,6 +257,20 @@ MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(Isolate* isolate,
|
||||
new_capacity);
|
||||
}
|
||||
|
||||
MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(
|
||||
Isolate* isolate, Handle<OrderedHashSet> table) {
|
||||
return OrderedHashTable<
|
||||
OrderedHashSet, OrderedHashSet::kEntrySizeWithoutChain>::Rehash(isolate,
|
||||
table);
|
||||
}
|
||||
|
||||
MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(
|
||||
Isolate* isolate, Handle<OrderedHashMap> table) {
|
||||
return OrderedHashTable<
|
||||
OrderedHashMap, OrderedHashMap::kEntrySizeWithoutChain>::Rehash(isolate,
|
||||
table);
|
||||
}
|
||||
|
||||
MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(Isolate* isolate,
|
||||
Handle<OrderedHashMap> table,
|
||||
int new_capacity) {
|
||||
|
||||
+7
@@ -138,6 +138,7 @@ class OrderedHashTable : public FixedArray {
|
||||
|
||||
// The extra +1 is for linking the bucket chains together.
|
||||
static const int kEntrySize = entrysize + 1;
|
||||
static const int kEntrySizeWithoutChain = entrysize;
|
||||
static const int kChainOffset = entrysize;
|
||||
|
||||
static const int kNotFound = -1;
|
||||
@@ -200,6 +201,8 @@ class OrderedHashTable : public FixedArray {
|
||||
static MaybeHandle<Derived> Allocate(
|
||||
Isolate* isolate, int capacity,
|
||||
AllocationType allocation = AllocationType::kYoung);
|
||||
|
||||
static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table);
|
||||
static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table,
|
||||
int new_capacity);
|
||||
|
||||
@@ -244,6 +247,8 @@ class V8_EXPORT_PRIVATE OrderedHashSet
|
||||
static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
|
||||
Handle<OrderedHashSet> table,
|
||||
int new_capacity);
|
||||
static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
|
||||
Handle<OrderedHashSet> table);
|
||||
static MaybeHandle<OrderedHashSet> Allocate(
|
||||
Isolate* isolate, int capacity,
|
||||
AllocationType allocation = AllocationType::kYoung);
|
||||
@@ -273,6 +278,8 @@ class V8_EXPORT_PRIVATE OrderedHashMap
|
||||
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
|
||||
Handle<OrderedHashMap> table,
|
||||
int new_capacity);
|
||||
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
|
||||
Handle<OrderedHashMap> table);
|
||||
Object ValueAt(int entry);
|
||||
|
||||
// This takes and returns raw Address values containing tagged Object
|
||||
|
||||
Vendored
+14
-2
@@ -70,7 +70,7 @@ void Deserializer::Initialize(Isolate* isolate) {
|
||||
void Deserializer::Rehash() {
|
||||
DCHECK(can_rehash() || deserializing_user_code());
|
||||
for (HeapObject item : to_rehash_) {
|
||||
item.RehashBasedOnMap(ReadOnlyRoots(isolate_));
|
||||
item.RehashBasedOnMap(isolate_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,14 @@ void Deserializer::DeserializeDeferredObjects() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When the deserialization of maps are deferred, they will be created
|
||||
// as filler maps, and we postpone the post processing until the maps
|
||||
// are also deserialized.
|
||||
for (const auto& pair : fillers_to_post_process_) {
|
||||
DCHECK(!pair.first.IsFiller());
|
||||
PostProcessNewObject(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
void Deserializer::LogNewObjectEvents() {
|
||||
@@ -201,7 +209,11 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj,
|
||||
DisallowHeapAllocation no_gc;
|
||||
|
||||
if ((FLAG_rehash_snapshot && can_rehash_) || deserializing_user_code()) {
|
||||
if (obj.IsString()) {
|
||||
if (obj.IsFiller()) {
|
||||
DCHECK_EQ(fillers_to_post_process_.find(obj),
|
||||
fillers_to_post_process_.end());
|
||||
fillers_to_post_process_.insert({obj, space});
|
||||
} else if (obj.IsString()) {
|
||||
// Uninitialize hash field as we need to recompute the hash.
|
||||
String string = String::cast(obj);
|
||||
string.set_hash_field(String::kEmptyHashField);
|
||||
|
||||
Vendored
+5
@@ -194,6 +194,11 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
|
||||
// TODO(6593): generalize rehashing, and remove this flag.
|
||||
bool can_rehash_;
|
||||
std::vector<HeapObject> to_rehash_;
|
||||
// Store the objects whose maps are deferred and thus initialized as filler
|
||||
// maps during deserialization, so that they can be processed later when the
|
||||
// maps become available.
|
||||
std::unordered_map<HeapObject, SnapshotSpace, Object::Hasher>
|
||||
fillers_to_post_process_;
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t num_api_references_;
|
||||
|
||||
+1
-1
@@ -48,9 +48,9 @@ MaybeHandle<HeapObject> ObjectDeserializer::Deserialize(Isolate* isolate) {
|
||||
LinkAllocationSites();
|
||||
LogNewMapEvents();
|
||||
result = handle(HeapObject::cast(root), isolate);
|
||||
Rehash();
|
||||
allocator()->RegisterDeserializedObjectsForBlackAllocation();
|
||||
}
|
||||
Rehash();
|
||||
CommitPostProcessedObjects();
|
||||
return scope.CloseAndEscape(result);
|
||||
}
|
||||
|
||||
+1
-1
@@ -57,12 +57,12 @@ MaybeHandle<Object> PartialDeserializer::Deserialize(
|
||||
// new code, which also has to be flushed from instruction cache.
|
||||
CHECK_EQ(start_address, code_space->top());
|
||||
|
||||
if (FLAG_rehash_snapshot && can_rehash()) Rehash();
|
||||
LogNewMapEvents();
|
||||
|
||||
result = handle(root, isolate);
|
||||
}
|
||||
|
||||
if (FLAG_rehash_snapshot && can_rehash()) Rehash();
|
||||
SetupOffHeapArrayBufferBackingStores();
|
||||
|
||||
return result;
|
||||
|
||||
+12
-5
@@ -3778,7 +3778,7 @@ UNINITIALIZED_TEST(SnapshotCreatorIncludeGlobalProxy) {
|
||||
FreeCurrentEmbeddedBlob();
|
||||
}
|
||||
|
||||
UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
|
||||
UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) {
|
||||
DisableAlwaysOpt();
|
||||
i::FLAG_rehash_snapshot = true;
|
||||
i::FLAG_hash_seed = 42;
|
||||
@@ -3796,13 +3796,18 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
|
||||
CompileRun(
|
||||
"var m = new Map();"
|
||||
"m.set('a', 1);"
|
||||
"m.set('b', 2);");
|
||||
"m.set('b', 2);"
|
||||
"var s = new Set();"
|
||||
"s.add(1);"
|
||||
"s.add(globalThis);");
|
||||
ExpectInt32("m.get('b')", 2);
|
||||
ExpectTrue("s.has(1)");
|
||||
ExpectTrue("s.has(globalThis)");
|
||||
creator.SetDefaultContext(context);
|
||||
}
|
||||
blob =
|
||||
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
||||
CHECK(!blob.CanBeRehashed());
|
||||
CHECK(blob.CanBeRehashed());
|
||||
}
|
||||
|
||||
ReadOnlyHeap::ClearSharedHeapForTest();
|
||||
@@ -3812,8 +3817,8 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
|
||||
create_params.snapshot_blob = &blob;
|
||||
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
||||
{
|
||||
// Check that no rehashing has been performed.
|
||||
CHECK_EQ(static_cast<uint64_t>(42),
|
||||
// Check that rehashing has been performed.
|
||||
CHECK_EQ(static_cast<uint64_t>(1337),
|
||||
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
|
||||
v8::Isolate::Scope isolate_scope(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
@@ -3821,6 +3826,8 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
|
||||
CHECK(!context.IsEmpty());
|
||||
v8::Context::Scope context_scope(context);
|
||||
ExpectInt32("m.get('b')", 2);
|
||||
ExpectTrue("s.has(1)");
|
||||
ExpectTrue("s.has(globalThis)");
|
||||
}
|
||||
isolate->Dispose();
|
||||
delete[] blob.data;
|
||||
|
||||
Reference in new issue
Block a user