}
+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();
}
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;
};
-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.
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
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
} 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(); \
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()) { \
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
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();
+}
--- /dev/null
+// 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");