aboutsummaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authorYang Guo <yangguo@chromium.org>2017-07-18 14:38:43 +0200
committerMyles Borins <mylesborins@google.com>2017-08-01 15:23:16 -0500
commit98ddab411523f83eca9b5311529f8288379e550f (patch)
treece31c41f17f9e32036eb433043152cf247c3bd81 /deps
parenta3c5ccd78ce2dbf6104f6145afd430654f125961 (diff)
downloadandroid-node-v8-98ddab411523f83eca9b5311529f8288379e550f.tar.gz
android-node-v8-98ddab411523f83eca9b5311529f8288379e550f.tar.bz2
android-node-v8-98ddab411523f83eca9b5311529f8288379e550f.zip
deps: backport rehash strings after deserialization
Original commit messages: https://github.com/v8/v8/commit/a2ab1353f6708b44d305fdd9fe65a6d29b95c6d6 [snapshot] Rehash strings after deserialization. See https://goo.gl/6aN8xA Bug: v8:6593 Change-Id: Ic8b0b57195d01d41591397d5d45de3f0f3ebc3d9 Reviewed-on: https://chromium-review.googlesource.com/574527 Reviewed-by: Camillo Bruni <cbruni@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Commit-Queue: Yang Guo <yangguo@chromium.org> Cr-Commit-Position: refs/heads/master@{#46732} https://github.com/v8/v8/commit/182caaf4a9b94024e47007d426831c024345cb97 Do not track transitions for built-in objects. Objects created during bootstrapping do not need a transition tree except for elements kind transitions. Bug: v8:6596 Change-Id: I237b8b2792f201336e1c9731c815095dd06bc182 Reviewed-on: https://chromium-review.googlesource.com/571750 Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Yang Guo <yangguo@chromium.org> Cr-Commit-Position: refs/heads/master@{#46693} Fixes: https://github.com/nodejs/node/issues/14171 Refs: https://github.com/nodejs/node/pull/14345 PR-URL: https://github.com/nodejs/node/pull/14004 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'deps')
-rw-r--r--deps/v8/src/api.cc13
-rw-r--r--deps/v8/src/bootstrapper.cc6
-rw-r--r--deps/v8/src/flag-definitions.h2
-rw-r--r--deps/v8/src/heap/heap.cc17
-rw-r--r--deps/v8/src/heap/heap.h3
-rw-r--r--deps/v8/src/js/array.js2
-rw-r--r--deps/v8/src/objects.cc11
-rw-r--r--deps/v8/src/snapshot/deserializer.cc71
-rw-r--r--deps/v8/src/snapshot/deserializer.h18
-rw-r--r--deps/v8/src/snapshot/partial-serializer.cc37
-rw-r--r--deps/v8/src/snapshot/partial-serializer.h8
-rw-r--r--deps/v8/src/snapshot/snapshot-common.cc13
-rw-r--r--deps/v8/src/snapshot/snapshot.h12
-rw-r--r--deps/v8/src/snapshot/startup-serializer.cc17
-rw-r--r--deps/v8/src/snapshot/startup-serializer.h8
-rw-r--r--deps/v8/src/transitions-inl.h2
-rw-r--r--deps/v8/src/transitions.cc42
-rw-r--r--deps/v8/src/transitions.h4
-rw-r--r--deps/v8/test/cctest/heap/test-heap.cc21
-rw-r--r--deps/v8/test/cctest/test-serialize.cc45
-rw-r--r--deps/v8/test/mjsunit/regress/regress-5902.js1
21 files changed, 321 insertions, 32 deletions
diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc
index 79008219a7..818dfa1e22 100644
--- a/deps/v8/src/api.cc
+++ b/deps/v8/src/api.cc
@@ -660,6 +660,9 @@ StartupData SnapshotCreator::CreateBlob(
isolate->heap()->SetSerializedGlobalProxySizes(*global_proxy_sizes);
}
+ // We might rehash strings and re-sort descriptors. Clear the lookup cache.
+ isolate->descriptor_lookup_cache()->Clear();
+
// If we don't do this then we end up with a stray root pointing at the
// context even after we have disposed of the context.
isolate->heap()->CollectAllAvailableGarbage(
@@ -701,11 +704,15 @@ StartupData SnapshotCreator::CreateBlob(
// Serialize each context with a new partial serializer.
i::List<i::SnapshotData*> context_snapshots(num_additional_contexts + 1);
+ // TODO(6593): generalize rehashing, and remove this flag.
+ bool can_be_rehashed = true;
+
{
// The default snapshot does not support embedder fields.
i::PartialSerializer partial_serializer(
isolate, &startup_serializer, v8::SerializeInternalFieldsCallback());
partial_serializer.Serialize(&default_context, false);
+ can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed();
context_snapshots.Add(new i::SnapshotData(&partial_serializer));
}
@@ -713,10 +720,12 @@ StartupData SnapshotCreator::CreateBlob(
i::PartialSerializer partial_serializer(
isolate, &startup_serializer, data->embedder_fields_serializers_[i]);
partial_serializer.Serialize(&contexts[i], true);
+ can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed();
context_snapshots.Add(new i::SnapshotData(&partial_serializer));
}
startup_serializer.SerializeWeakReferencesAndDeferred();
+ can_be_rehashed = can_be_rehashed && startup_serializer.can_be_rehashed();
#ifdef DEBUG
if (i::FLAG_external_reference_stats) {
@@ -725,8 +734,8 @@ StartupData SnapshotCreator::CreateBlob(
#endif // DEBUG
i::SnapshotData startup_snapshot(&startup_serializer);
- StartupData result =
- i::Snapshot::CreateSnapshotBlob(&startup_snapshot, &context_snapshots);
+ StartupData result = i::Snapshot::CreateSnapshotBlob(
+ &startup_snapshot, &context_snapshots, can_be_rehashed);
// Delete heap-allocated context snapshot instances.
for (const auto& context_snapshot : context_snapshots) {
diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc
index b1fda971cf..2652ab028e 100644
--- a/deps/v8/src/bootstrapper.cc
+++ b/deps/v8/src/bootstrapper.cc
@@ -652,6 +652,8 @@ Handle<JSFunction> Genesis::GetThrowTypeErrorIntrinsic(
DCHECK(false);
}
+ JSObject::MigrateSlowToFast(function, 0, "Bootstrapping");
+
return function;
}
@@ -1397,6 +1399,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
sloppy_function_map_writable_prototype_->SetConstructor(*function_fun);
strict_function_map_writable_prototype_->SetConstructor(*function_fun);
class_function_map_->SetConstructor(*function_fun);
+
+ JSObject::MigrateSlowToFast(function_fun, 0, "Bootstrapping");
}
{
@@ -2221,6 +2225,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
info->set_length(1);
native_context()->set_promise_reject_shared_fun(*info);
}
+
+ JSObject::MigrateSlowToFast(promise_fun, 0, "Bootstrapping");
}
{ // -- R e g E x p
diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h
index 85ee27ee6e..f719555c5f 100644
--- a/deps/v8/src/flag-definitions.h
+++ b/deps/v8/src/flag-definitions.h
@@ -1002,6 +1002,8 @@ DEFINE_BOOL(abort_on_stack_overflow, false,
DEFINE_BOOL(randomize_hashes, true,
"randomize hashes to avoid predictable hash collisions "
"(with snapshots this option cannot override the baked-in seed)")
+DEFINE_BOOL(rehash_snapshot, true,
+ "rehash strings from the snapshot to override the baked-in seed")
DEFINE_INT(hash_seed, 0,
"Fixed seed to use to hash property keys (0 means random)"
"(with snapshots this option cannot override the baked-in seed)")
diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc
index ad3bfef559..2b26e0d1b9 100644
--- a/deps/v8/src/heap/heap.cc
+++ b/deps/v8/src/heap/heap.cc
@@ -5679,14 +5679,7 @@ bool Heap::SetUp() {
// Set up the seed that is used to randomize the string hash function.
DCHECK(hash_seed() == 0);
- if (FLAG_randomize_hashes) {
- if (FLAG_hash_seed == 0) {
- int rnd = isolate()->random_number_generator()->NextInt();
- set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask));
- } else {
- set_hash_seed(Smi::FromInt(FLAG_hash_seed));
- }
- }
+ if (FLAG_randomize_hashes) InitializeHashSeed();
for (int i = 0; i < static_cast<int>(v8::Isolate::kUseCounterFeatureCount);
i++) {
@@ -5731,6 +5724,14 @@ bool Heap::SetUp() {
return true;
}
+void Heap::InitializeHashSeed() {
+ if (FLAG_hash_seed == 0) {
+ int rnd = isolate()->random_number_generator()->NextInt();
+ set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask));
+ } else {
+ set_hash_seed(Smi::FromInt(FLAG_hash_seed));
+ }
+}
bool Heap::CreateHeapObjects() {
// Create initial maps.
diff --git a/deps/v8/src/heap/heap.h b/deps/v8/src/heap/heap.h
index 80bc68c172..f18f3edd3f 100644
--- a/deps/v8/src/heap/heap.h
+++ b/deps/v8/src/heap/heap.h
@@ -1000,6 +1000,9 @@ class Heap {
// without actually creating any objects.
bool SetUp();
+ // (Re-)Initialize hash seed from flag or RNG.
+ void InitializeHashSeed();
+
// Bootstraps the object heap with the core set of objects required to run.
// Returns whether it succeeded.
bool CreateHeapObjects();
diff --git a/deps/v8/src/js/array.js b/deps/v8/src/js/array.js
index 188f21c41e..2e22a521dc 100644
--- a/deps/v8/src/js/array.js
+++ b/deps/v8/src/js/array.js
@@ -1282,6 +1282,8 @@ var unscopables = {
keys: true,
};
+%ToFastProperties(unscopables);
+
%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
DONT_ENUM | READ_ONLY);
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index e2748a5216..a1d87342ea 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -8860,7 +8860,13 @@ void Map::TraceAllTransitions(Map* map) {
void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child,
Handle<Name> name, SimpleTransitionFlag flag) {
- if (!parent->GetBackPointer()->IsUndefined(parent->GetIsolate())) {
+ Isolate* isolate = parent->GetIsolate();
+ // Do not track transitions during bootstrap except for element transitions.
+ if (isolate->bootstrapper()->IsActive() &&
+ !name.is_identical_to(isolate->factory()->elements_transition_symbol())) {
+ return;
+ }
+ if (!parent->GetBackPointer()->IsUndefined(isolate)) {
parent->set_owns_descriptors(false);
} else {
// |parent| is initial map and it must keep the ownership, there must be no
@@ -16712,6 +16718,9 @@ template class Dictionary<UnseededNumberDictionary,
UnseededNumberDictionaryShape,
uint32_t>;
+template void
+HashTable<GlobalDictionary, GlobalDictionaryShape, Handle<Name> >::Rehash(Handle<Name> key);
+
template Handle<SeededNumberDictionary>
Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::New(
Isolate*, int at_least_space_for, PretenureFlag pretenure,
diff --git a/deps/v8/src/snapshot/deserializer.cc b/deps/v8/src/snapshot/deserializer.cc
index c76e4eca54..b39f351a94 100644
--- a/deps/v8/src/snapshot/deserializer.cc
+++ b/deps/v8/src/snapshot/deserializer.cc
@@ -125,6 +125,7 @@ void Deserializer::Deserialize(Isolate* isolate) {
LOG_CODE_EVENT(isolate_, LogCompiledFunctions());
isolate_->builtins()->MarkInitialized();
+ if (FLAG_rehash_snapshot && can_rehash_) Rehash();
}
MaybeHandle<Object> Deserializer::DeserializePartial(
@@ -155,6 +156,9 @@ MaybeHandle<Object> Deserializer::DeserializePartial(
// changed and logging should be added to notify the profiler et al of the
// new code, which also has to be flushed from instruction cache.
CHECK_EQ(start_address, code_space->top());
+
+ if (FLAG_rehash_snapshot && can_rehash_) RehashContext(Context::cast(root));
+
return Handle<Object>(root, isolate);
}
@@ -181,6 +185,63 @@ MaybeHandle<HeapObject> Deserializer::DeserializeObject(Isolate* isolate) {
}
}
+// We only really just need HashForObject here.
+class StringRehashKey : public HashTableKey {
+ public:
+ uint32_t HashForObject(Object* other) override {
+ return String::cast(other)->Hash();
+ }
+
+ static uint32_t StringHash(Object* obj) {
+ UNREACHABLE();
+ return String::cast(obj)->Hash();
+ }
+
+ bool IsMatch(Object* string) override {
+ UNREACHABLE();
+ return false;
+ }
+
+ uint32_t Hash() override {
+ UNREACHABLE();
+ return 0;
+ }
+
+ Handle<Object> AsHandle(Isolate* isolate) override {
+ UNREACHABLE();
+ return isolate->factory()->empty_string();
+ }
+};
+
+void Deserializer::Rehash() {
+ DCHECK(can_rehash_);
+ isolate_->heap()->InitializeHashSeed();
+ if (FLAG_profile_deserialization) {
+ PrintF("Re-initializing hash seed to %x\n",
+ isolate_->heap()->hash_seed()->value());
+ }
+ StringRehashKey string_rehash_key;
+ isolate_->heap()->string_table()->Rehash(&string_rehash_key);
+ SortMapDescriptors();
+}
+
+void Deserializer::RehashContext(Context* context) {
+ DCHECK(can_rehash_);
+ for (const auto& array : transition_arrays_) array->Sort();
+ Handle<Name> dummy = isolate_->factory()->empty_string();
+ context->global_object()->global_dictionary()->Rehash(dummy);
+ SortMapDescriptors();
+}
+
+void Deserializer::SortMapDescriptors() {
+ for (const auto& address : allocated_maps_) {
+ Map* map = Map::cast(HeapObject::FromAddress(address));
+ if (map->instance_descriptors()->number_of_descriptors() > 1) {
+ map->instance_descriptors()->Sort();
+ }
+ }
+}
+
Deserializer::~Deserializer() {
#ifdef DEBUG
// Do not perform checks if we aborted deserialization.
@@ -371,6 +432,16 @@ HeapObject* Deserializer::PostProcessNewObject(HeapObject* obj, int space) {
string->resource()));
isolate_->heap()->RegisterExternalString(string);
}
+ if (FLAG_rehash_snapshot && can_rehash_ && !deserializing_user_code()) {
+ if (obj->IsString()) {
+ // Uninitialize hash field as we are going to reinitialize the hash seed.
+ String* string = String::cast(obj);
+ string->set_hash_field(String::kEmptyHashField);
+ } else if (obj->IsTransitionArray() &&
+ TransitionArray::cast(obj)->number_of_entries() > 1) {
+ transition_arrays_.Add(TransitionArray::cast(obj));
+ }
+ }
// Check alignment.
DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment()));
return obj;
diff --git a/deps/v8/src/snapshot/deserializer.h b/deps/v8/src/snapshot/deserializer.h
index a56adb67d4..3a650d9974 100644
--- a/deps/v8/src/snapshot/deserializer.h
+++ b/deps/v8/src/snapshot/deserializer.h
@@ -39,7 +39,8 @@ class Deserializer : public SerializerDeserializer {
external_reference_table_(NULL),
deserialized_large_objects_(0),
deserializing_user_code_(deserializing_user_code),
- next_alignment_(kWordAligned) {
+ next_alignment_(kWordAligned),
+ can_rehash_(false) {
DecodeReservation(data->Reservations());
}
@@ -62,6 +63,8 @@ class Deserializer : public SerializerDeserializer {
attached_objects_.Add(attached_object);
}
+ void SetRehashability(bool v) { can_rehash_ = v; }
+
private:
void VisitRootPointers(Root root, Object** start, Object** end) override;
@@ -115,6 +118,15 @@ class Deserializer : public SerializerDeserializer {
// snapshot by chunk index and offset.
HeapObject* GetBackReferencedObject(int space);
+ // Rehash after deserializing an isolate.
+ void Rehash();
+
+ // Rehash after deserializing a context.
+ void RehashContext(Context* context);
+
+ // Sort descriptors of deserialized maps using new string hashes.
+ void SortMapDescriptors();
+
// Cached current isolate.
Isolate* isolate_;
@@ -142,11 +154,15 @@ class Deserializer : public SerializerDeserializer {
List<AccessorInfo*> accessor_infos_;
List<Handle<String> > new_internalized_strings_;
List<Handle<Script> > new_scripts_;
+ List<TransitionArray*> transition_arrays_;
bool deserializing_user_code_;
AllocationAlignment next_alignment_;
+ // TODO(6593): generalize rehashing, and remove this flag.
+ bool can_rehash_;
+
DISALLOW_COPY_AND_ASSIGN(Deserializer);
};
diff --git a/deps/v8/src/snapshot/partial-serializer.cc b/deps/v8/src/snapshot/partial-serializer.cc
index d3e60e0e4e..bbd829eddf 100644
--- a/deps/v8/src/snapshot/partial-serializer.cc
+++ b/deps/v8/src/snapshot/partial-serializer.cc
@@ -15,7 +15,9 @@ PartialSerializer::PartialSerializer(
v8::SerializeEmbedderFieldsCallback callback)
: Serializer(isolate),
startup_serializer_(startup_serializer),
- serialize_embedder_fields_(callback) {
+ serialize_embedder_fields_(callback),
+ rehashable_global_dictionary_(nullptr),
+ can_be_rehashed_(true) {
InitializeCodeAddressMap();
}
@@ -24,7 +26,7 @@ PartialSerializer::~PartialSerializer() {
}
void PartialSerializer::Serialize(Object** o, bool include_global_proxy) {
- if ((*o)->IsContext()) {
+ if ((*o)->IsNativeContext()) {
Context* context = Context::cast(*o);
reference_map()->AddAttachedReference(context->global_proxy());
// The bootstrap snapshot has a code-stub context. When serializing the
@@ -32,14 +34,18 @@ void PartialSerializer::Serialize(Object** o, bool include_global_proxy) {
// and it's next context pointer may point to the code-stub context. Clear
// it before serializing, it will get re-added to the context list
// explicitly when it's loaded.
- if (context->IsNativeContext()) {
- context->set(Context::NEXT_CONTEXT_LINK,
- isolate_->heap()->undefined_value());
- DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
- // Reset math random cache to get fresh random numbers.
- context->set_math_random_index(Smi::kZero);
- context->set_math_random_cache(isolate_->heap()->undefined_value());
- }
+ context->set(Context::NEXT_CONTEXT_LINK,
+ isolate_->heap()->undefined_value());
+ DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
+ // Reset math random cache to get fresh random numbers.
+ context->set_math_random_index(Smi::kZero);
+ context->set_math_random_cache(isolate_->heap()->undefined_value());
+ DCHECK_NULL(rehashable_global_dictionary_);
+ rehashable_global_dictionary_ =
+ context->global_object()->global_dictionary();
+ } else {
+ // We only do rehashing for native contexts.
+ can_be_rehashed_ = false;
}
VisitRootPointer(Root::kPartialSnapshotCache, o);
SerializeDeferredObjects();
@@ -104,6 +110,8 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
}
}
+ if (obj->IsHashTable()) CheckRehashability(obj);
+
// Object has not yet been serialized. Serialize it here.
ObjectSerializer serializer(this, obj, &sink_, how_to_code, where_to_point);
serializer.Serialize();
@@ -152,5 +160,14 @@ void PartialSerializer::SerializeEmbedderFields() {
sink_.Put(kSynchronize, "Finished with embedder fields data");
}
+void PartialSerializer::CheckRehashability(HeapObject* table) {
+ DCHECK(table->IsHashTable());
+ if (!can_be_rehashed_) return;
+ // We can only correctly rehash if the global dictionary is the only hash
+ // table that we deserialize.
+ if (table == rehashable_global_dictionary_) return;
+ can_be_rehashed_ = false;
+}
+
} // namespace internal
} // namespace v8
diff --git a/deps/v8/src/snapshot/partial-serializer.h b/deps/v8/src/snapshot/partial-serializer.h
index 313a800042..4b3035f9e5 100644
--- a/deps/v8/src/snapshot/partial-serializer.h
+++ b/deps/v8/src/snapshot/partial-serializer.h
@@ -23,6 +23,8 @@ class PartialSerializer : public Serializer {
// Serialize the objects reachable from a single object pointer.
void Serialize(Object** o, bool include_global_proxy);
+ bool can_be_rehashed() const { return can_be_rehashed_; }
+
private:
void SerializeObject(HeapObject* o, HowToCode how_to_code,
WhereToPoint where_to_point, int skip) override;
@@ -31,9 +33,15 @@ class PartialSerializer : public Serializer {
void SerializeEmbedderFields();
+ void CheckRehashability(HeapObject* table);
+
StartupSerializer* startup_serializer_;
List<JSObject*> embedder_field_holders_;
v8::SerializeEmbedderFieldsCallback serialize_embedder_fields_;
+ GlobalDictionary* rehashable_global_dictionary_;
+ // Indicates whether we only serialized hash tables that we can rehash.
+ // TODO(yangguo): generalize rehashing, and remove this flag.
+ bool can_be_rehashed_;
DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
};
diff --git a/deps/v8/src/snapshot/snapshot-common.cc b/deps/v8/src/snapshot/snapshot-common.cc
index 9350ec6b54..9f299e697e 100644
--- a/deps/v8/src/snapshot/snapshot-common.cc
+++ b/deps/v8/src/snapshot/snapshot-common.cc
@@ -41,6 +41,7 @@ bool Snapshot::Initialize(Isolate* isolate) {
Vector<const byte> startup_data = ExtractStartupData(blob);
SnapshotData snapshot_data(startup_data);
Deserializer deserializer(&snapshot_data);
+ deserializer.SetRehashability(ExtractRehashability(blob));
bool success = isolate->Init(&deserializer);
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
@@ -62,6 +63,7 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
ExtractContextData(blob, static_cast<int>(context_index));
SnapshotData snapshot_data(context_data);
Deserializer deserializer(&snapshot_data);
+ deserializer.SetRehashability(ExtractRehashability(blob));
MaybeHandle<Object> maybe_context = deserializer.DeserializePartial(
isolate, global_proxy, embedder_fields_deserializer);
@@ -98,7 +100,7 @@ void ProfileDeserialization(const SnapshotData* startup_snapshot,
v8::StartupData Snapshot::CreateSnapshotBlob(
const SnapshotData* startup_snapshot,
- const List<SnapshotData*>* context_snapshots) {
+ const List<SnapshotData*>* context_snapshots, bool can_be_rehashed) {
int num_contexts = context_snapshots->length();
int startup_snapshot_offset = StartupSnapshotOffset(num_contexts);
int total_length = startup_snapshot_offset;
@@ -111,6 +113,8 @@ v8::StartupData Snapshot::CreateSnapshotBlob(
char* data = new char[total_length];
memcpy(data + kNumberOfContextsOffset, &num_contexts, kInt32Size);
+ int rehashability = can_be_rehashed ? 1 : 0;
+ memcpy(data + kRehashabilityOffset, &rehashability, kInt32Size);
int payload_offset = StartupSnapshotOffset(num_contexts);
int payload_length = startup_snapshot->RawData().length();
memcpy(data + payload_offset, startup_snapshot->RawData().start(),
@@ -143,6 +147,13 @@ int Snapshot::ExtractNumContexts(const v8::StartupData* data) {
return num_contexts;
}
+bool Snapshot::ExtractRehashability(const v8::StartupData* data) {
+ CHECK_LT(kRehashabilityOffset, data->raw_size);
+ int rehashability;
+ memcpy(&rehashability, data->data + kRehashabilityOffset, kInt32Size);
+ return rehashability != 0;
+}
+
Vector<const byte> Snapshot::ExtractStartupData(const v8::StartupData* data) {
int num_contexts = ExtractNumContexts(data);
int startup_offset = StartupSnapshotOffset(num_contexts);
diff --git a/deps/v8/src/snapshot/snapshot.h b/deps/v8/src/snapshot/snapshot.h
index 7d9082e6d2..91d60cfa89 100644
--- a/deps/v8/src/snapshot/snapshot.h
+++ b/deps/v8/src/snapshot/snapshot.h
@@ -71,7 +71,7 @@ class Snapshot : public AllStatic {
static v8::StartupData CreateSnapshotBlob(
const SnapshotData* startup_snapshot,
- const List<SnapshotData*>* context_snapshots);
+ const List<SnapshotData*>* context_snapshots, bool can_be_rehashed);
#ifdef DEBUG
static bool SnapshotIsValid(v8::StartupData* snapshot_blob);
@@ -79,14 +79,16 @@ class Snapshot : public AllStatic {
private:
static int ExtractNumContexts(const v8::StartupData* data);
+ static bool ExtractRehashability(const v8::StartupData* data);
static Vector<const byte> ExtractStartupData(const v8::StartupData* data);
static Vector<const byte> ExtractContextData(const v8::StartupData* data,
int index);
// Snapshot blob layout:
// [0] number of contexts N
- // [1] offset to context 0
- // [2] offset to context 1
+ // [1] rehashability
+ // [2] offset to context 0
+ // [3] offset to context 1
// ...
// ... offset to context N - 1
// ... startup snapshot data
@@ -94,8 +96,10 @@ class Snapshot : public AllStatic {
// ... context 1 snapshot data
static const int kNumberOfContextsOffset = 0;
+ // TODO(yangguo): generalize rehashing, and remove this flag.
+ static const int kRehashabilityOffset = kNumberOfContextsOffset + kInt32Size;
static const int kFirstContextOffsetOffset =
- kNumberOfContextsOffset + kInt32Size;
+ kRehashabilityOffset + kInt32Size;
static int StartupSnapshotOffset(int num_contexts) {
return kFirstContextOffsetOffset + num_contexts * kInt32Size;
diff --git a/deps/v8/src/snapshot/startup-serializer.cc b/deps/v8/src/snapshot/startup-serializer.cc
index dfc02036d8..89eaaf55e4 100644
--- a/deps/v8/src/snapshot/startup-serializer.cc
+++ b/deps/v8/src/snapshot/startup-serializer.cc
@@ -16,7 +16,8 @@ StartupSerializer::StartupSerializer(
: Serializer(isolate),
clear_function_code_(function_code_handling ==
v8::SnapshotCreator::FunctionCodeHandling::kClear),
- serializing_builtins_(false) {
+ serializing_builtins_(false),
+ can_be_rehashed_(true) {
InitializeCodeAddressMap();
}
@@ -78,6 +79,8 @@ void StartupSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
isolate_->heap()->uninitialized_symbol());
}
+ if (obj->IsHashTable()) CheckRehashability(obj);
+
// Object has not yet been serialized. Serialize it here.
ObjectSerializer object_serializer(this, obj, &sink_, how_to_code,
where_to_point);
@@ -189,5 +192,17 @@ bool StartupSerializer::RootShouldBeSkipped(int root_index) {
serializing_immortal_immovables_roots_;
}
+void StartupSerializer::CheckRehashability(HeapObject* table) {
+ DCHECK(table->IsHashTable());
+ if (!can_be_rehashed_) return;
+ // We can only correctly rehash if the four hash tables below are the only
+ // ones that we deserialize.
+ if (table == isolate_->heap()->empty_slow_element_dictionary()) return;
+ if (table == isolate_->heap()->empty_properties_dictionary()) return;
+ if (table == isolate_->heap()->weak_object_to_code_table()) return;
+ if (table == isolate_->heap()->string_table()) return;
+ can_be_rehashed_ = false;
+}
+
} // namespace internal
} // namespace v8
diff --git a/deps/v8/src/snapshot/startup-serializer.h b/deps/v8/src/snapshot/startup-serializer.h
index 223e1c7bff..c58454d066 100644
--- a/deps/v8/src/snapshot/startup-serializer.h
+++ b/deps/v8/src/snapshot/startup-serializer.h
@@ -29,6 +29,8 @@ class StartupSerializer : public Serializer {
int PartialSnapshotCacheIndex(HeapObject* o);
+ bool can_be_rehashed() const { return can_be_rehashed_; }
+
private:
class PartialCacheIndexMap {
public:
@@ -68,12 +70,18 @@ class StartupSerializer : public Serializer {
// roots. In the second pass, we serialize the rest.
bool RootShouldBeSkipped(int root_index);
+ void CheckRehashability(HeapObject* hashtable);
+
bool clear_function_code_;
bool serializing_builtins_;
bool serializing_immortal_immovables_roots_;
std::bitset<Heap::kStrongRootListLength> root_has_been_serialized_;
PartialCacheIndexMap partial_cache_index_map_;
List<AccessorInfo*> accessor_infos_;
+ // Indicates whether we only serialized hash tables that we can rehash.
+ // TODO(yangguo): generalize rehashing, and remove this flag.
+ bool can_be_rehashed_;
+
DISALLOW_COPY_AND_ASSIGN(StartupSerializer);
};
diff --git a/deps/v8/src/transitions-inl.h b/deps/v8/src/transitions-inl.h
index df28c2c991..694674dc0f 100644
--- a/deps/v8/src/transitions-inl.h
+++ b/deps/v8/src/transitions-inl.h
@@ -106,7 +106,6 @@ int TransitionArray::SearchName(Name* name, int* out_insertion_index) {
}
-#ifdef DEBUG
bool TransitionArray::IsSpecialTransition(Name* name) {
if (!name->IsSymbol()) return false;
Heap* heap = name->GetHeap();
@@ -115,7 +114,6 @@ bool TransitionArray::IsSpecialTransition(Name* name) {
name == heap->elements_transition_symbol() ||
name == heap->strict_function_transition_symbol();
}
-#endif
int TransitionArray::CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1,
diff --git a/deps/v8/src/transitions.cc b/deps/v8/src/transitions.cc
index 5333fa6e25..42d1c89507 100644
--- a/deps/v8/src/transitions.cc
+++ b/deps/v8/src/transitions.cc
@@ -551,5 +551,47 @@ int TransitionArray::Search(PropertyKind kind, Name* name,
if (transition == kNotFound) return kNotFound;
return SearchDetails(transition, kind, attributes, out_insertion_index);
}
+
+void TransitionArray::Sort() {
+ DisallowHeapAllocation no_gc;
+ // In-place insertion sort.
+ int length = number_of_transitions();
+ for (int i = 1; i < length; i++) {
+ Name* key = GetKey(i);
+ Map* target = GetTarget(i);
+ PropertyKind kind = kData;
+ PropertyAttributes attributes = NONE;
+ if (!IsSpecialTransition(key)) {
+ PropertyDetails details = GetTargetDetails(key, target);
+ kind = details.kind();
+ attributes = details.attributes();
+ }
+ int j;
+ for (j = i - 1; j >= 0; j--) {
+ Name* temp_key = GetKey(j);
+ Map* temp_target = GetTarget(j);
+ PropertyKind temp_kind = kData;
+ PropertyAttributes temp_attributes = NONE;
+ if (!IsSpecialTransition(temp_key)) {
+ PropertyDetails details = GetTargetDetails(temp_key, temp_target);
+ temp_kind = details.kind();
+ temp_attributes = details.attributes();
+ }
+ int cmp =
+ CompareKeys(temp_key, temp_key->Hash(), temp_kind, temp_attributes,
+ key, key->Hash(), kind, attributes);
+ if (cmp > 0) {
+ SetKey(j + 1, temp_key);
+ SetTarget(j + 1, temp_target);
+ } else {
+ break;
+ }
+ }
+ SetKey(j + 1, key);
+ SetTarget(j + 1, target);
+ }
+ DCHECK(IsSortedNoDuplicates());
+}
+
} // namespace internal
} // namespace v8
diff --git a/deps/v8/src/transitions.h b/deps/v8/src/transitions.h
index e553a05ce4..e40bbb0468 100644
--- a/deps/v8/src/transitions.h
+++ b/deps/v8/src/transitions.h
@@ -190,15 +190,17 @@ class TransitionArray: public FixedArray {
void TransitionArrayVerify();
#endif
+ void Sort();
+
#ifdef DEBUG
bool IsSortedNoDuplicates(int valid_entries = -1);
static bool IsSortedNoDuplicates(Map* map);
static bool IsConsistentWithBackPointers(Map* map);
+#endif
// Returns true for a non-property transitions like elements kind, observed
// or frozen transitions.
static inline bool IsSpecialTransition(Name* name);
-#endif
// Constant for denoting key was not found.
static const int kNotFound = -1;
diff --git a/deps/v8/test/cctest/heap/test-heap.cc b/deps/v8/test/cctest/heap/test-heap.cc
index fc2b68e809..afa62b9f57 100644
--- a/deps/v8/test/cctest/heap/test-heap.cc
+++ b/deps/v8/test/cctest/heap/test-heap.cc
@@ -6730,5 +6730,26 @@ HEAP_TEST(Regress5831) {
CHECK(chunk->NeverEvacuate());
}
+UNINITIALIZED_TEST(ReinitializeStringHashSeed) {
+ // Enable rehashing and create an isolate and context.
+ i::FLAG_rehash_snapshot = true;
+ for (int i = 1; i < 3; i++) {
+ i::FLAG_hash_seed = 1337 * i;
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
+ v8::Isolate* isolate = v8::Isolate::New(create_params);
+ {
+ v8::Isolate::Scope isolate_scope(isolate);
+ CHECK_EQ(1337 * i,
+ reinterpret_cast<i::Isolate*>(isolate)->heap()->HashSeed());
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ CHECK(!context.IsEmpty());
+ v8::Context::Scope context_scope(context);
+ }
+ isolate->Dispose();
+ }
+}
+
} // namespace internal
} // namespace v8
diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc
index 058dd55d3c..1ab6e7bfd8 100644
--- a/deps/v8/test/cctest/test-serialize.cc
+++ b/deps/v8/test/cctest/test-serialize.cc
@@ -2547,6 +2547,51 @@ TEST(SnapshotCreatorIncludeGlobalProxy) {
delete[] blob.data;
}
+UNINITIALIZED_TEST(ReinitializeStringHashSeedNotRehashable) {
+ DisableAlwaysOpt();
+ i::FLAG_rehash_snapshot = true;
+ i::FLAG_hash_seed = 42;
+ i::FLAG_allow_natives_syntax = true;
+ v8::StartupData blob;
+ {
+ v8::SnapshotCreator creator;
+ v8::Isolate* isolate = creator.GetIsolate();
+ {
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ v8::Context::Scope context_scope(context);
+ // Create dictionary mode object.
+ CompileRun(
+ "var a = {};"
+ "a.b = 1;"
+ "a.c = 2;"
+ "delete a.b;");
+ ExpectInt32("a.c", 2);
+ creator.SetDefaultContext(context);
+ }
+ blob =
+ creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
+ }
+
+ i::FLAG_hash_seed = 1337;
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
+ create_params.snapshot_blob = &blob;
+ v8::Isolate* isolate = v8::Isolate::New(create_params);
+ {
+ // Check that no rehashing has been performed.
+ CHECK_EQ(42, reinterpret_cast<i::Isolate*>(isolate)->heap()->HashSeed());
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ CHECK(!context.IsEmpty());
+ v8::Context::Scope context_scope(context);
+ ExpectInt32("a.c", 2);
+ }
+ isolate->Dispose();
+ delete[] blob.data;
+}
+
TEST(SerializationMemoryStats) {
FLAG_profile_deserialization = true;
FLAG_always_opt = false;
diff --git a/deps/v8/test/mjsunit/regress/regress-5902.js b/deps/v8/test/mjsunit/regress/regress-5902.js
index 034b6a7951..69dde4387a 100644
--- a/deps/v8/test/mjsunit/regress/regress-5902.js
+++ b/deps/v8/test/mjsunit/regress/regress-5902.js
@@ -58,6 +58,5 @@ assertEquals(
'Error.prototype',
'EvalError.prototype', 'RangeError.prototype', 'ReferenceError.prototype',
'SyntaxError.prototype', 'TypeError.prototype', 'URIError.prototype',
- 'Map', 'Map.prototype.constructor', 'Set', 'Set.prototype.constructor'
],
log);