Correctly hook up back references to internalized strings in code deserializer.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 23 Jul 2014 07:16:32 +0000 (07:16 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 23 Jul 2014 07:16:32 +0000 (07:16 +0000)
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
src/objects.h
src/serialize.cc
src/serialize.h
test/cctest/test-serialize.cc
test/mjsunit/deserialize-reference.js [new file with mode: 0644]

index 0aca5cf..36e7eb1 100644 (file)
@@ -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();
 }
index 46787bf..629a951 100644 (file)
@@ -9310,6 +9310,11 @@ class String: public Name {
   static Handle<FixedArray> CalculateLineEnds(Handle<String> 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;
index 2031935..4156dfb 100644 (file)
@@ -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<Address>(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()) {              \
index 0676c39..c980567 100644 (file)
@@ -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
index 115b4e4..2ecc75b 100644 (file)
@@ -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<v8::Context> context = v8::Context::New(isolate);
+    v8::Context::Scope context_scope(context);
+
+    v8::Local<v8::String> source_str = v8_str(source);
+    v8::ScriptOrigin origin(v8_str("test"));
+    v8::ScriptCompiler::Source source(source_str, origin);
+    v8::Local<v8::UnboundScript> script = v8::ScriptCompiler::CompileUnbound(
+        isolate, &source, v8::ScriptCompiler::kProduceCodeCache);
+    const v8::ScriptCompiler::CachedData* data = source.GetCachedData();
+    // Persist cached data.
+    uint8_t* buffer = NewArray<uint8_t>(data->length);
+    MemCopy(buffer, data->data, data->length);
+    cache = new v8::ScriptCompiler::CachedData(
+        buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);
+
+    v8::Local<v8::Value> 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<v8::Context> context = v8::Context::New(isolate);
+    v8::Context::Scope context_scope(context);
+
+    v8::Local<v8::String> source_str = v8_str(source);
+    v8::ScriptOrigin origin(v8_str("test"));
+    v8::ScriptCompiler::Source source(source_str, origin, cache);
+    v8::Local<v8::UnboundScript> script;
+    {
+      DisallowCompilation no_compile(reinterpret_cast<Isolate*>(isolate));
+      script = v8::ScriptCompiler::CompileUnbound(
+          isolate, &source, v8::ScriptCompiler::kConsumeCodeCache);
+    }
+    v8::Local<v8::Value> 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 (file)
index 0000000..b032013
--- /dev/null
@@ -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");