// Similarly, we want to use the global that has been created by the templates
// passed through the API. The global from the snapshot is detached from the
// other objects in the snapshot.
- void HookUpGlobalObject(Handle<GlobalObject> global_object);
+ void HookUpGlobalObject(Handle<GlobalObject> global_object,
+ Handle<FixedArray> outdated_contexts);
// New context initialization. Used for creating a context from scratch.
void InitializeGlobal(Handle<GlobalObject> global_object,
Handle<JSFunction> empty_function);
}
-void Genesis::HookUpGlobalObject(Handle<GlobalObject> global_object) {
+void Genesis::HookUpGlobalObject(Handle<GlobalObject> global_object,
+ Handle<FixedArray> outdated_contexts) {
Handle<GlobalObject> global_object_from_snapshot(
GlobalObject::cast(native_context()->extension()));
Handle<JSBuiltinsObject> builtins_global(native_context()->builtins());
native_context()->set_extension(*global_object);
- native_context()->set_global_object(*global_object);
native_context()->set_security_token(*global_object);
+
+ // Replace outdated global objects in deserialized contexts.
+ for (int i = 0; i < outdated_contexts->length(); ++i) {
+ Context* context = Context::cast(outdated_contexts->get(i));
+ DCHECK_EQ(context->global_object(), *global_object_from_snapshot);
+ context->set_global_object(*global_object);
+ }
+
static const PropertyAttributes attributes =
static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
Runtime::DefineObjectProperty(builtins_global, factory()->global_string(),
// We can only de-serialize a context if the isolate was initialized from
// a snapshot. Otherwise we have to build the context from scratch.
- if (isolate->initialized_from_snapshot()) {
- native_context_ = Snapshot::NewContextFromSnapshot(isolate);
- } else {
+ Handle<FixedArray> outdated_contexts;
+ if (!isolate->initialized_from_snapshot() ||
+ !Snapshot::NewContextFromSnapshot(isolate, &outdated_contexts)
+ .ToHandle(&native_context_)) {
native_context_ = Handle<Context>();
}
global_proxy_template, maybe_global_proxy, &global_object);
HookUpGlobalProxy(global_object, global_proxy);
- HookUpGlobalObject(global_object);
+ HookUpGlobalObject(global_object, outdated_contexts);
native_context()->builtins()->set_global_proxy(
native_context()->global_proxy());
bool Deserializer::ReserveSpace() {
+#ifdef DEBUG
+ for (int i = NEW_SPACE; i < kNumberOfSpaces; ++i) {
+ CHECK(reservations_[i].length() > 0);
+ }
+#endif // DEBUG
if (!isolate_->heap()->ReserveSpace(reservations_)) return false;
for (int i = 0; i < kNumberOfPreallocatedSpaces; i++) {
high_water_[i] = reservations_[i][0].start;
}
-void Deserializer::Deserialize(Isolate* isolate) {
+void Deserializer::Initialize(Isolate* isolate) {
+ DCHECK_EQ(NULL, isolate_);
+ DCHECK_NE(NULL, isolate);
isolate_ = isolate;
- DCHECK(isolate_ != NULL);
+ DCHECK_EQ(NULL, external_reference_decoder_);
+ external_reference_decoder_ = new ExternalReferenceDecoder(isolate);
+}
+
+
+void Deserializer::Deserialize(Isolate* isolate) {
+ Initialize(isolate);
if (!ReserveSpace()) FatalProcessOutOfMemory("deserializing context");
// No active threads.
DCHECK_EQ(NULL, isolate_->thread_manager()->FirstThreadStateInUse());
// No active handles.
DCHECK(isolate_->handle_scope_implementer()->blocks()->is_empty());
- DCHECK_EQ(NULL, external_reference_decoder_);
- external_reference_decoder_ = new ExternalReferenceDecoder(isolate);
isolate_->heap()->IterateSmiRoots(this);
isolate_->heap()->IterateStrongRoots(this, VISIT_ONLY_STRONG);
isolate_->heap()->RepairFreeListsAfterBoot();
}
-void Deserializer::DeserializePartial(Isolate* isolate, Object** root,
- OnOOM on_oom) {
- isolate_ = isolate;
- for (int i = NEW_SPACE; i < kNumberOfSpaces; i++) {
- DCHECK(reservations_[i].length() > 0);
- }
+MaybeHandle<Object> Deserializer::DeserializePartial(
+ Isolate* isolate, Handle<FixedArray>* outdated_contexts_out) {
+ Initialize(isolate);
if (!ReserveSpace()) {
- if (on_oom == FATAL_ON_OOM) FatalProcessOutOfMemory("deserialize context");
- *root = NULL;
- return;
- }
- if (external_reference_decoder_ == NULL) {
- external_reference_decoder_ = new ExternalReferenceDecoder(isolate);
+ FatalProcessOutOfMemory("deserialize context");
+ return MaybeHandle<Object>();
}
DisallowHeapAllocation no_gc;
-
// Keep track of the code space start and end pointers in case new
// code objects were unserialized
OldSpace* code_space = isolate_->heap()->code_space();
Address start_address = code_space->top();
- VisitPointer(root);
+ Object* root;
+ Object* outdated_contexts;
+ VisitPointer(&root);
+ VisitPointer(&outdated_contexts);
// There's no code deserialized here. If this assert fires
// then that's changed and logging should be added to notify
// the profiler et al of the new code.
CHECK_EQ(start_address, code_space->top());
+ CHECK(outdated_contexts->IsFixedArray());
+ *outdated_contexts_out =
+ Handle<FixedArray>(FixedArray::cast(outdated_contexts), isolate);
+ return Handle<Object>(root, isolate);
+}
+
+
+MaybeHandle<SharedFunctionInfo> Deserializer::DeserializeCode(
+ Isolate* isolate) {
+ Initialize(isolate);
+ if (!ReserveSpace()) {
+ return Handle<SharedFunctionInfo>();
+ } else {
+ DisallowHeapAllocation no_gc;
+ Object* root;
+ VisitPointer(&root);
+ return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(root));
+ }
}
}
-void PartialSerializer::Serialize(Object** object) {
- this->VisitPointer(object);
+void PartialSerializer::Serialize(Object** o) {
+ if ((*o)->IsContext()) global_object_ = Context::cast(*o)->global_object();
+ VisitPointer(o);
+ SerializeOutdatedContextsAsFixedArray();
Pad();
}
+void PartialSerializer::SerializeOutdatedContextsAsFixedArray() {
+ int length = outdated_contexts_.length();
+ if (length == 0) {
+ FixedArray* empty = isolate_->heap()->empty_fixed_array();
+ SerializeObject(empty, kPlain, kStartOfObject, 0);
+ } else {
+ // Serialize an imaginary fixed array containing outdated contexts.
+ int size = FixedArray::SizeFor(length);
+ Allocate(NEW_SPACE, size);
+ sink_->Put(kNewObject + NEW_SPACE, "emulated FixedArray");
+ sink_->PutInt(size >> kObjectAlignmentBits, "FixedArray size in words");
+ Map* map = isolate_->heap()->fixed_array_map();
+ SerializeObject(map, kPlain, kStartOfObject, 0);
+ Smi* length_smi = Smi::FromInt(length);
+ sink_->Put(kOnePointerRawData, "Smi");
+ for (int i = 0; i < kPointerSize; i++) {
+ sink_->Put(reinterpret_cast<byte*>(&length_smi)[i], "Byte");
+ }
+ for (int i = 0; i < length; i++) {
+ BackReference back_ref = outdated_contexts_[i];
+ sink_->Put(kBackref + back_ref.space(), "BackRef");
+ sink_->PutInt(back_ref.reference(), "BackRefValue");
+ }
+ }
+}
+
+
bool Serializer::ShouldBeSkipped(Object** current) {
Object** roots = isolate()->heap()->roots_array_start();
return current == &roots[Heap::kStoreBufferTopRootIndex]
// Object has not yet been serialized. Serialize it here.
ObjectSerializer serializer(this, obj, sink_, how_to_code, where_to_point);
serializer.Serialize();
+
+ if (obj->IsContext() &&
+ Context::cast(obj)->global_object() == global_object_) {
+ // Context refers to the current global object. This reference will
+ // become outdated after deserialization.
+ BackReference back_reference = back_reference_map_.Lookup(obj);
+ DCHECK(back_reference.is_valid());
+ outdated_contexts_.Add(back_reference);
+ }
}
base::ElapsedTimer timer;
if (FLAG_profile_deserialization) timer.Start();
- Object* root;
+ HandleScope scope(isolate);
- {
- HandleScope scope(isolate);
+ SmartPointer<SerializedCodeData> scd(
+ SerializedCodeData::FromCachedData(cached_data, *source));
+ if (scd.is_empty()) {
+ if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n");
+ DCHECK(cached_data->rejected());
+ return MaybeHandle<SharedFunctionInfo>();
+ }
- SmartPointer<SerializedCodeData> scd(
- SerializedCodeData::FromCachedData(cached_data, *source));
- if (scd.is_empty()) {
- if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n");
- DCHECK(cached_data->rejected());
- return MaybeHandle<SharedFunctionInfo>();
- }
+ // Eagerly expand string table to avoid allocations during deserialization.
+ StringTable::EnsureCapacityForDeserialization(isolate,
+ scd->NumInternalizedStrings());
- // Eagerly expand string table to avoid allocations during deserialization.
- StringTable::EnsureCapacityForDeserialization(
- isolate, scd->NumInternalizedStrings());
-
- // Prepare and register list of attached objects.
- Vector<const uint32_t> code_stub_keys = scd->CodeStubKeys();
- Vector<Handle<Object> > attached_objects = Vector<Handle<Object> >::New(
- code_stub_keys.length() + kCodeStubsBaseIndex);
- attached_objects[kSourceObjectIndex] = source;
- for (int i = 0; i < code_stub_keys.length(); i++) {
- attached_objects[i + kCodeStubsBaseIndex] =
- CodeStub::GetCode(isolate, code_stub_keys[i]).ToHandleChecked();
- }
+ // Prepare and register list of attached objects.
+ Vector<const uint32_t> code_stub_keys = scd->CodeStubKeys();
+ Vector<Handle<Object> > attached_objects = Vector<Handle<Object> >::New(
+ code_stub_keys.length() + kCodeStubsBaseIndex);
+ attached_objects[kSourceObjectIndex] = source;
+ for (int i = 0; i < code_stub_keys.length(); i++) {
+ attached_objects[i + kCodeStubsBaseIndex] =
+ CodeStub::GetCode(isolate, code_stub_keys[i]).ToHandleChecked();
+ }
- Deserializer deserializer(scd.get());
- deserializer.SetAttachedObjects(&attached_objects);
+ Deserializer deserializer(scd.get());
+ deserializer.SetAttachedObjects(&attached_objects);
- // Deserialize.
- deserializer.DeserializePartial(isolate, &root, Deserializer::NULL_ON_OOM);
- if (root == NULL) {
- // Deserializing may fail if the reservations cannot be fulfilled.
- if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n");
- return MaybeHandle<SharedFunctionInfo>();
- }
- deserializer.FlushICacheForNewCodeObjects();
+ // Deserialize.
+ Handle<SharedFunctionInfo> result;
+ if (!deserializer.DeserializeCode(isolate).ToHandle(&result)) {
+ // Deserializing may fail if the reservations cannot be fulfilled.
+ if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n");
+ return MaybeHandle<SharedFunctionInfo>();
}
+ deserializer.FlushICacheForNewCodeObjects();
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int length = cached_data->length();
PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
}
- Handle<SharedFunctionInfo> result(SharedFunctionInfo::cast(root), isolate);
result->set_deserialized(true);
if (isolate->logger()->is_logging_code_events() ||
*result, NULL, name);
}
- return result;
+ return scope.CloseAndEscape(result);
}
// Deserialize the snapshot into an empty heap.
void Deserialize(Isolate* isolate);
- enum OnOOM { FATAL_ON_OOM, NULL_ON_OOM };
-
// Deserialize a single object and the objects reachable from it.
// We may want to abort gracefully even if deserialization fails.
- void DeserializePartial(Isolate* isolate, Object** root,
- OnOOM on_oom = FATAL_ON_OOM);
+ MaybeHandle<Object> DeserializePartial(
+ Isolate* isolate, Handle<FixedArray>* outdated_contexts_out);
+
+ MaybeHandle<SharedFunctionInfo> DeserializeCode(Isolate* isolate);
void FlushICacheForNewCodeObjects();
UNREACHABLE();
}
+ void Initialize(Isolate* isolate);
+
void DecodeReservation(Vector<const SerializedData::Reservation> res);
bool ReserveSpace();
class PartialSerializer : public Serializer {
public:
- PartialSerializer(Isolate* isolate,
- Serializer* startup_snapshot_serializer,
+ PartialSerializer(Isolate* isolate, Serializer* startup_snapshot_serializer,
SnapshotByteSink* sink)
- : Serializer(isolate, sink),
- startup_serializer_(startup_snapshot_serializer) {
+ : Serializer(isolate, sink),
+ startup_serializer_(startup_snapshot_serializer),
+ outdated_contexts_(0),
+ global_object_(NULL) {
InitializeCodeAddressMap();
}
startup_serializer_->isolate()->heap()->fixed_cow_array_map();
}
+ void SerializeOutdatedContextsAsFixedArray();
Serializer* startup_serializer_;
+ List<BackReference> outdated_contexts_;
+ Object* global_object_;
DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
};
}
-Handle<Context> Snapshot::NewContextFromSnapshot(Isolate* isolate) {
+MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
+ Isolate* isolate, Handle<FixedArray>* outdated_contexts_out) {
if (!HaveASnapshotToStartFrom()) return Handle<Context>();
base::ElapsedTimer timer;
if (FLAG_profile_deserialization) timer.Start();
Vector<const byte> context_data = ExtractContextData(&blob);
SnapshotData snapshot_data(context_data);
Deserializer deserializer(&snapshot_data);
- Object* root;
- deserializer.DeserializePartial(isolate, &root);
- CHECK(root->IsContext());
+
+ MaybeHandle<Object> maybe_context =
+ deserializer.DeserializePartial(isolate, outdated_contexts_out);
+ Handle<Object> result;
+ if (!maybe_context.ToHandle(&result)) return MaybeHandle<Context>();
+ CHECK(result->IsContext());
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int bytes = context_data.length();
PrintF("[Deserializing context (%d bytes) took %0.3f ms]\n", bytes, ms);
}
- return Handle<Context>(Context::cast(root));
+ return Handle<Context>::cast(result);
}
// snapshot could be found.
static bool Initialize(Isolate* isolate);
// Create a new context using the internal partial snapshot.
- static Handle<Context> NewContextFromSnapshot(Isolate* isolate);
+ static MaybeHandle<Context> NewContextFromSnapshot(
+ Isolate* isolate, Handle<FixedArray>* outdated_contexts_out);
static bool HaveASnapshotToStartFrom();
byte* snapshot = ReadBytes(file_name, &snapshot_size);
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
- Object* root;
+ HandleScope handle_scope(isolate);
+ Handle<Object> root;
+ Handle<FixedArray> outdated_contexts;
{
SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
Deserializer deserializer(&snapshot_data);
- deserializer.DeserializePartial(isolate, &root);
+ root = deserializer.DeserializePartial(isolate, &outdated_contexts)
+ .ToHandleChecked();
+ CHECK_EQ(0, outdated_contexts->length());
CHECK(root->IsString());
}
- HandleScope handle_scope(isolate);
- Handle<Object> root_handle(root, isolate);
-
- Object* root2;
+ Handle<Object> root2;
{
SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
Deserializer deserializer(&snapshot_data);
- deserializer.DeserializePartial(isolate, &root2);
+ root2 = deserializer.DeserializePartial(isolate, &outdated_contexts)
+ .ToHandleChecked();
CHECK(root2->IsString());
- CHECK(*root_handle == root2);
+ CHECK(root.is_identical_to(root2));
}
}
v8_isolate->Dispose();
byte* snapshot = ReadBytes(file_name, &snapshot_size);
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
- Object* root;
+ HandleScope handle_scope(isolate);
+ Handle<Object> root;
+ Handle<FixedArray> outdated_contexts;
{
SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
Deserializer deserializer(&snapshot_data);
- deserializer.DeserializePartial(isolate, &root);
+ root = deserializer.DeserializePartial(isolate, &outdated_contexts)
+ .ToHandleChecked();
CHECK(root->IsContext());
+ CHECK_EQ(1, outdated_contexts->length());
}
- HandleScope handle_scope(isolate);
- Handle<Object> root_handle(root, isolate);
-
- Object* root2;
+ Handle<Object> root2;
{
SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
Deserializer deserializer(&snapshot_data);
- deserializer.DeserializePartial(isolate, &root2);
+ root2 = deserializer.DeserializePartial(isolate, &outdated_contexts)
+ .ToHandleChecked();
CHECK(root2->IsContext());
- CHECK(*root_handle != root2);
+ CHECK(!root.is_identical_to(root2));
+ }
+ }
+ v8_isolate->Dispose();
+ }
+}
+
+
+UNINITIALIZED_TEST(CustomContextSerialization) {
+ if (!Snapshot::HaveASnapshotToStartFrom()) {
+ v8::Isolate::CreateParams params;
+ params.enable_serializer = true;
+ v8::Isolate* v8_isolate = v8::Isolate::New(params);
+ Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
+ Heap* heap = isolate->heap();
+ {
+ v8::Isolate::Scope isolate_scope(v8_isolate);
+
+ v8::Persistent<v8::Context> env;
+ {
+ HandleScope scope(isolate);
+ env.Reset(v8_isolate, v8::Context::New(v8_isolate));
+ }
+ DCHECK(!env.IsEmpty());
+ {
+ v8::HandleScope handle_scope(v8_isolate);
+ v8::Local<v8::Context>::New(v8_isolate, env)->Enter();
+ // After execution, e's function context refers to the global object.
+ CompileRun(
+ "var e;"
+ "(function() {"
+ " e = function(s) { eval (s); }"
+ "})();");
+ }
+ // Make sure all builtin scripts are cached.
+ {
+ HandleScope scope(isolate);
+ for (int i = 0; i < Natives::GetBuiltinsCount(); i++) {
+ isolate->bootstrapper()->NativesSourceLookup(i);
+ }
+ }
+ // If we don't do this then we end up with a stray root pointing at the
+ // context even after we have disposed of env.
+ heap->CollectAllGarbage(Heap::kNoGCFlags);
+
+ int file_name_length = StrLength(FLAG_testing_serialization_file) + 10;
+ Vector<char> startup_name = Vector<char>::New(file_name_length + 1);
+ SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file);
+
+ {
+ v8::HandleScope handle_scope(v8_isolate);
+ v8::Local<v8::Context>::New(v8_isolate, env)->Exit();
+ }
+
+ i::Object* raw_context = *v8::Utils::OpenPersistent(env);
+
+ env.Reset();
+
+ SnapshotByteSink startup_sink;
+ StartupSerializer startup_serializer(isolate, &startup_sink);
+ startup_serializer.SerializeStrongReferences();
+
+ SnapshotByteSink partial_sink;
+ PartialSerializer partial_serializer(isolate, &startup_serializer,
+ &partial_sink);
+ partial_serializer.Serialize(&raw_context);
+ startup_serializer.SerializeWeakReferences();
+
+ SnapshotData startup_snapshot(startup_sink, startup_serializer);
+ SnapshotData partial_snapshot(partial_sink, partial_serializer);
+
+ WritePayload(partial_snapshot.RawData(), FLAG_testing_serialization_file);
+ WritePayload(startup_snapshot.RawData(), startup_name.start());
+
+ startup_name.Dispose();
+ }
+ v8_isolate->Dispose();
+ }
+}
+
+
+UNINITIALIZED_DEPENDENT_TEST(CustomContextDeSerialization,
+ CustomContextSerialization) {
+ if (!Snapshot::HaveASnapshotToStartFrom()) {
+ int file_name_length = StrLength(FLAG_testing_serialization_file) + 10;
+ Vector<char> startup_name = Vector<char>::New(file_name_length + 1);
+ SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file);
+
+ v8::Isolate* v8_isolate = InitializeFromFile(startup_name.start());
+ CHECK(v8_isolate);
+ startup_name.Dispose();
+ {
+ v8::Isolate::Scope isolate_scope(v8_isolate);
+
+ const char* file_name = FLAG_testing_serialization_file;
+
+ int snapshot_size = 0;
+ byte* snapshot = ReadBytes(file_name, &snapshot_size);
+
+ Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
+ HandleScope handle_scope(isolate);
+ Handle<Object> root;
+ Handle<FixedArray> outdated_contexts;
+ {
+ SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
+ Deserializer deserializer(&snapshot_data);
+ root = deserializer.DeserializePartial(isolate, &outdated_contexts)
+ .ToHandleChecked();
+ CHECK_EQ(2, outdated_contexts->length());
+ CHECK(root->IsContext());
}
}
v8_isolate->Dispose();