From ed04566cba2ac21e5294ef75c7cc58c3b82304c2 Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Wed, 23 Jul 2014 07:16:32 +0000 Subject: [PATCH] Correctly hook up back references to internalized strings in code deserializer. R=mvstanton@chromium.org Review URL: https://codereview.chromium.org/411483002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22540 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/objects-inl.h | 26 +++++++++++++ src/objects.h | 5 +++ src/serialize.cc | 22 +++++++++-- src/serialize.h | 4 +- test/cctest/test-serialize.cc | 53 +++++++++++++++++++++++++++ test/mjsunit/deserialize-reference.js | 8 ++++ 6 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 test/mjsunit/deserialize-reference.js diff --git a/src/objects-inl.h b/src/objects-inl.h index 0aca5cfcdb..36e7eb1758 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -6580,6 +6580,32 @@ bool String::AsArrayIndex(uint32_t* index) { } +void String::SetForwardedInternalizedString(String* canonical) { + ASSERT(IsInternalizedString()); + ASSERT(HasHashCode()); + if (canonical == this) return; // No need to forward. + ASSERT(SlowEquals(canonical)); + ASSERT(canonical->IsInternalizedString()); + ASSERT(canonical->HasHashCode()); + WRITE_FIELD(this, kHashFieldOffset, canonical); + // Setting the hash field to a tagged value sets the LSB, causing the hash + // code to be interpreted as uninitialized. We use this fact to recognize + // that we have a forwarded string. + ASSERT(!HasHashCode()); +} + + +String* String::GetForwardedInternalizedString() { + ASSERT(IsInternalizedString()); + if (HasHashCode()) return this; + String* canonical = String::cast(READ_FIELD(this, kHashFieldOffset)); + ASSERT(canonical->IsInternalizedString()); + ASSERT(SlowEquals(canonical)); + ASSERT(canonical->HasHashCode()); + return canonical; +} + + Object* JSReceiver::GetConstructor() { return map()->constructor(); } diff --git a/src/objects.h b/src/objects.h index 46787bf120..629a951314 100644 --- a/src/objects.h +++ b/src/objects.h @@ -9310,6 +9310,11 @@ class String: public Name { static Handle CalculateLineEnds(Handle string, bool include_ending_line); + // Use the hash field to forward to the canonical internalized string + // when deserializing an internalized string. + inline void SetForwardedInternalizedString(String* string); + inline String* GetForwardedInternalizedString(); + private: friend class Name; friend class StringTableInsertionKey; diff --git a/src/serialize.cc b/src/serialize.cc index 20319357ab..4156dfbf62 100644 --- a/src/serialize.cc +++ b/src/serialize.cc @@ -867,7 +867,7 @@ class StringTableInsertionKey : public HashTableKey { }; -HeapObject* Deserializer::ProcessObjectFromSerializedCode(HeapObject* obj) { +HeapObject* Deserializer::ProcessNewObjectFromSerializedCode(HeapObject* obj) { if (obj->IsString()) { String* string = String::cast(obj); // Uninitialize hash field as the hash seed may have changed. @@ -876,13 +876,23 @@ HeapObject* Deserializer::ProcessObjectFromSerializedCode(HeapObject* obj) { DisallowHeapAllocation no_gc; HandleScope scope(isolate_); StringTableInsertionKey key(string); - return *StringTable::LookupKey(isolate_, &key); + String* canonical = *StringTable::LookupKey(isolate_, &key); + string->SetForwardedInternalizedString(canonical); + return canonical; } } return obj; } +Object* Deserializer::ProcessBackRefInSerializedCode(Object* obj) { + if (obj->IsInternalizedString()) { + return String::cast(obj)->GetForwardedInternalizedString(); + } + return obj; +} + + // This routine writes the new object into the pointer provided and then // returns true if the new object was in young space and false otherwise. // The reason for this strange interface is that otherwise the object is @@ -907,7 +917,7 @@ void Deserializer::ReadObject(int space_number, if (obj->IsAllocationSite()) RelinkAllocationSite(AllocationSite::cast(obj)); // Fix up strings from serialized user code. - if (deserializing_user_code()) obj = ProcessObjectFromSerializedCode(obj); + if (deserializing_user_code()) obj = ProcessNewObjectFromSerializedCode(obj); *write_back = obj; #ifdef DEBUG @@ -972,6 +982,9 @@ void Deserializer::ReadChunk(Object** current, } else if (where == kBackref) { \ emit_write_barrier = (space_number == NEW_SPACE); \ new_object = GetAddressFromEnd(data & kSpaceMask); \ + if (deserializing_user_code()) { \ + new_object = ProcessBackRefInSerializedCode(new_object); \ + } \ } else if (where == kBuiltin) { \ ASSERT(deserializing_user_code()); \ int builtin_id = source_->GetInt(); \ @@ -992,6 +1005,9 @@ void Deserializer::ReadChunk(Object** current, reinterpret_cast
(current) + skip); \ emit_write_barrier = (space_number == NEW_SPACE); \ new_object = GetAddressFromEnd(data & kSpaceMask); \ + if (deserializing_user_code()) { \ + new_object = ProcessBackRefInSerializedCode(new_object); \ + } \ } \ if (within == kInnerPointer) { \ if (space_number != CODE_SPACE || new_object->IsCode()) { \ diff --git a/src/serialize.h b/src/serialize.h index 0676c39ed1..c9805678e2 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -280,7 +280,9 @@ class Deserializer: public SerializerDeserializer { Object** start, Object** end, int space, Address object_address); void ReadObject(int space_number, Object** write_back); - HeapObject* ProcessObjectFromSerializedCode(HeapObject* obj); + // Special handling for serialized code like hooking up internalized strings. + HeapObject* ProcessNewObjectFromSerializedCode(HeapObject* obj); + Object* ProcessBackRefInSerializedCode(Object* obj); // This routine both allocates a new object, and also keeps // track of where objects have been allocated so that we can diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc index 115b4e4f03..2ecc75b1cb 100644 --- a/test/cctest/test-serialize.cc +++ b/test/cctest/test-serialize.cc @@ -791,3 +791,56 @@ TEST(SerializeToplevelInternalizedString) { delete cache; } + + +TEST(SerializeToplevelIsolates) { + FLAG_serialize_toplevel = true; + + const char* source = "function f() { return 'abc'; }; f() + 'def'"; + v8::ScriptCompiler::CachedData* cache; + + v8::Isolate* isolate = v8::Isolate::New(); + { + v8::Isolate::Scope iscope(isolate); + v8::HandleScope scope(isolate); + v8::Local context = v8::Context::New(isolate); + v8::Context::Scope context_scope(context); + + v8::Local source_str = v8_str(source); + v8::ScriptOrigin origin(v8_str("test")); + v8::ScriptCompiler::Source source(source_str, origin); + v8::Local script = v8::ScriptCompiler::CompileUnbound( + isolate, &source, v8::ScriptCompiler::kProduceCodeCache); + const v8::ScriptCompiler::CachedData* data = source.GetCachedData(); + // Persist cached data. + uint8_t* buffer = NewArray(data->length); + MemCopy(buffer, data->data, data->length); + cache = new v8::ScriptCompiler::CachedData( + buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned); + + v8::Local result = script->BindToCurrentContext()->Run(); + CHECK(result->ToString()->Equals(v8_str("abcdef"))); + } + isolate->Dispose(); + + isolate = v8::Isolate::New(); + { + v8::Isolate::Scope iscope(isolate); + v8::HandleScope scope(isolate); + v8::Local context = v8::Context::New(isolate); + v8::Context::Scope context_scope(context); + + v8::Local source_str = v8_str(source); + v8::ScriptOrigin origin(v8_str("test")); + v8::ScriptCompiler::Source source(source_str, origin, cache); + v8::Local script; + { + DisallowCompilation no_compile(reinterpret_cast(isolate)); + script = v8::ScriptCompiler::CompileUnbound( + isolate, &source, v8::ScriptCompiler::kConsumeCodeCache); + } + v8::Local result = script->BindToCurrentContext()->Run(); + CHECK(result->ToString()->Equals(v8_str("abcdef"))); + } + isolate->Dispose(); +} diff --git a/test/mjsunit/deserialize-reference.js b/test/mjsunit/deserialize-reference.js new file mode 100644 index 0000000000..b032013159 --- /dev/null +++ b/test/mjsunit/deserialize-reference.js @@ -0,0 +1,8 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --cache=code --serialize-toplevel + +var a = "123"; +assertEquals(a, "123");