From 78f1aff6c95f320525483ef2250a250747f66105 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 29 May 2020 20:51:31 +0800 Subject: [PATCH] deps: V8: backport 22014de00115 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 > Reviewed-by: Jakob Gruber > Reviewed-by: Camillo Bruni > 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 Reviewed-by: Camillo Bruni Reviewed-by: Jakob Gruber Commit-Queue: Joyee Cheung 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 Reviewed-By: Michaƫl Zasso --- common.gypi | 2 +- deps/v8/src/heap/factory.cc | 6 +++- deps/v8/src/objects/heap-object.h | 2 +- deps/v8/src/objects/js-collection.h | 2 ++ deps/v8/src/objects/objects.cc | 38 +++++++++++++++++--- deps/v8/src/objects/ordered-hash-table.cc | 21 +++++++++++ deps/v8/src/objects/ordered-hash-table.h | 7 ++++ deps/v8/src/snapshot/context-deserializer.cc | 2 +- deps/v8/src/snapshot/deserializer.cc | 16 +++++++-- deps/v8/src/snapshot/deserializer.h | 5 +++ deps/v8/src/snapshot/object-deserializer.cc | 2 +- deps/v8/test/cctest/test-serialize.cc | 17 ++++++--- 12 files changed, 104 insertions(+), 16 deletions(-) diff --git a/common.gypi b/common.gypi index b866224e15f829..15947ffb36645b 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.9', + 'v8_embedder_string': '-node.10', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 25825f35f79571..7e4f897fee4ffc 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -2772,8 +2772,12 @@ Handle Factory::NewUninitializedJSGlobalProxy(int size) { map->set_is_access_check_needed(true); map->set_may_have_interesting_symbols(true); LOG(isolate(), MapDetails(*map)); - return Handle::cast( + Handle proxy = Handle::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 object, diff --git a/deps/v8/src/objects/heap-object.h b/deps/v8/src/objects/heap-object.h index b19d429320b5be..74fe664ca35d6d 100644 --- a/deps/v8/src/objects/heap-object.h +++ b/deps/v8/src/objects/heap-object.h @@ -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) \ diff --git a/deps/v8/src/objects/js-collection.h b/deps/v8/src/objects/js-collection.h index 17f9c3e198ba8b..a0350726c02db7 100644 --- a/deps/v8/src/objects/js-collection.h +++ b/deps/v8/src/objects/js-collection.h @@ -30,6 +30,7 @@ class JSSet : public TorqueGeneratedJSSet { public: static void Initialize(Handle set, Isolate* isolate); static void Clear(Isolate* isolate, Handle set); + void Rehash(Isolate* isolate); // Dispatched behavior. DECL_PRINTER(JSSet) @@ -56,6 +57,7 @@ class JSMap : public TorqueGeneratedJSMap { public: static void Initialize(Handle map, Isolate* isolate); static void Clear(Isolate* isolate, Handle map); + void Rehash(Isolate* isolate); // Dispatched behavior. DECL_PRINTER(JSMap) diff --git a/deps/v8/src/objects/objects.cc b/deps/v8/src/objects/objects.cc index 53693149e142e1..3eb78f46301f2b 100644 --- a/deps/v8/src/objects/objects.cc +++ b/deps/v8/src/objects/objects.cc @@ -2306,9 +2306,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: @@ -2318,6 +2317,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; @@ -2327,10 +2328,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: @@ -2354,7 +2358,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(); @@ -2386,6 +2391,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; @@ -7852,6 +7868,13 @@ void JSSet::Clear(Isolate* isolate, Handle set) { set->set_table(*table); } +void JSSet::Rehash(Isolate* isolate) { + Handle table_handle(OrderedHashSet::cast(table()), isolate); + Handle new_table = + OrderedHashSet::Rehash(isolate, table_handle).ToHandleChecked(); + set_table(*new_table); +} + void JSMap::Initialize(Handle map, Isolate* isolate) { Handle table = isolate->factory()->NewOrderedHashMap(); map->set_table(*table); @@ -7863,6 +7886,13 @@ void JSMap::Clear(Isolate* isolate, Handle map) { map->set_table(*table); } +void JSMap::Rehash(Isolate* isolate) { + Handle table_handle(OrderedHashMap::cast(table()), isolate); + Handle new_table = + OrderedHashMap::Rehash(isolate, table_handle).ToHandleChecked(); + set_table(*new_table); +} + void JSWeakCollection::Initialize(Handle weak_collection, Isolate* isolate) { Handle table = EphemeronHashTable::New(isolate, 0); diff --git a/deps/v8/src/objects/ordered-hash-table.cc b/deps/v8/src/objects/ordered-hash-table.cc index cbf3ba373b9c24..d3250bd92db8d2 100644 --- a/deps/v8/src/objects/ordered-hash-table.cc +++ b/deps/v8/src/objects/ordered-hash-table.cc @@ -194,6 +194,13 @@ HeapObject OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) { return ro_roots.empty_ordered_hash_map(); } +template +MaybeHandle OrderedHashTable::Rehash( + Isolate* isolate, Handle table) { + return OrderedHashTable::Rehash(isolate, table, + table->Capacity()); +} + template MaybeHandle OrderedHashTable::Rehash( Isolate* isolate, Handle table, int new_capacity) { @@ -250,6 +257,20 @@ MaybeHandle OrderedHashSet::Rehash(Isolate* isolate, new_capacity); } +MaybeHandle OrderedHashSet::Rehash( + Isolate* isolate, Handle table) { + return OrderedHashTable< + OrderedHashSet, OrderedHashSet::kEntrySizeWithoutChain>::Rehash(isolate, + table); +} + +MaybeHandle OrderedHashMap::Rehash( + Isolate* isolate, Handle table) { + return OrderedHashTable< + OrderedHashMap, OrderedHashMap::kEntrySizeWithoutChain>::Rehash(isolate, + table); +} + MaybeHandle OrderedHashMap::Rehash(Isolate* isolate, Handle table, int new_capacity) { diff --git a/deps/v8/src/objects/ordered-hash-table.h b/deps/v8/src/objects/ordered-hash-table.h index b587960432caf5..5f3c45a110aa48 100644 --- a/deps/v8/src/objects/ordered-hash-table.h +++ b/deps/v8/src/objects/ordered-hash-table.h @@ -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 Allocate( Isolate* isolate, int capacity, AllocationType allocation = AllocationType::kYoung); + + static MaybeHandle Rehash(Isolate* isolate, Handle table); static MaybeHandle Rehash(Isolate* isolate, Handle table, int new_capacity); @@ -244,6 +247,8 @@ class V8_EXPORT_PRIVATE OrderedHashSet static MaybeHandle Rehash(Isolate* isolate, Handle table, int new_capacity); + static MaybeHandle Rehash(Isolate* isolate, + Handle table); static MaybeHandle Allocate( Isolate* isolate, int capacity, AllocationType allocation = AllocationType::kYoung); @@ -273,6 +278,8 @@ class V8_EXPORT_PRIVATE OrderedHashMap static MaybeHandle Rehash(Isolate* isolate, Handle table, int new_capacity); + static MaybeHandle Rehash(Isolate* isolate, + Handle table); Object ValueAt(int entry); // This takes and returns raw Address values containing tagged Object diff --git a/deps/v8/src/snapshot/context-deserializer.cc b/deps/v8/src/snapshot/context-deserializer.cc index 2a3d77646a9a87..b6b6da54f91810 100644 --- a/deps/v8/src/snapshot/context-deserializer.cc +++ b/deps/v8/src/snapshot/context-deserializer.cc @@ -59,12 +59,12 @@ MaybeHandle ContextDeserializer::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; diff --git a/deps/v8/src/snapshot/deserializer.cc b/deps/v8/src/snapshot/deserializer.cc index 33e4db439312db..1f502b26103d5c 100644 --- a/deps/v8/src/snapshot/deserializer.cc +++ b/deps/v8/src/snapshot/deserializer.cc @@ -82,7 +82,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_); } } @@ -142,6 +142,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() { @@ -213,7 +221,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); diff --git a/deps/v8/src/snapshot/deserializer.h b/deps/v8/src/snapshot/deserializer.h index 3af3eca5910986..72ca72977191d6 100644 --- a/deps/v8/src/snapshot/deserializer.h +++ b/deps/v8/src/snapshot/deserializer.h @@ -197,6 +197,11 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer { // TODO(6593): generalize rehashing, and remove this flag. bool can_rehash_; std::vector 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 + fillers_to_post_process_; #ifdef DEBUG uint32_t num_api_references_; diff --git a/deps/v8/src/snapshot/object-deserializer.cc b/deps/v8/src/snapshot/object-deserializer.cc index 2de08846d4c883..cb72aa3410454b 100644 --- a/deps/v8/src/snapshot/object-deserializer.cc +++ b/deps/v8/src/snapshot/object-deserializer.cc @@ -47,9 +47,9 @@ MaybeHandle ObjectDeserializer::Deserialize(Isolate* isolate) { LinkAllocationSites(); LogNewMapEvents(); result = handle(HeapObject::cast(root), isolate); - Rehash(); allocator()->RegisterDeserializedObjectsForBlackAllocation(); } + Rehash(); CommitPostProcessedObjects(); return scope.CloseAndEscape(result); } diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index 5b7ec576cf2e9e..c7e57ad81b7fcc 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -3755,7 +3755,7 @@ UNINITIALIZED_TEST(SnapshotCreatorIncludeGlobalProxy) { FreeCurrentEmbeddedBlob(); } -UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { +UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) { DisableAlwaysOpt(); i::FLAG_rehash_snapshot = true; i::FLAG_hash_seed = 42; @@ -3773,13 +3773,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()); } i::FLAG_hash_seed = 1337; @@ -3788,8 +3793,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(42), + // Check that rehashing has been performed. + CHECK_EQ(static_cast(1337), HashSeed(reinterpret_cast(isolate))); v8::Isolate::Scope isolate_scope(isolate); v8::HandleScope handle_scope(isolate); @@ -3797,6 +3802,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;