Update references to global object after deserializing context.
authoryangguo <yangguo@chromium.org>
Tue, 13 Jan 2015 08:48:00 +0000 (00:48 -0800)
committerCommit bot <commit-bot@chromium.org>
Tue, 13 Jan 2015 08:48:08 +0000 (08:48 +0000)
R=jochen@chromium.org
TEST=test-serialize/CustomContext{Des,S}erialization

Also test by
- make ia32.debug embedscript=<full path to mjsunit.js>
- d8 -e "assertDoesNotThrow('print(1)')"

Review URL: https://codereview.chromium.org/846023002

Cr-Commit-Position: refs/heads/master@{#26035}

src/bootstrapper.cc
src/serialize.cc
src/serialize.h
src/snapshot-common.cc
src/snapshot.h
test/cctest/test-serialize.cc

index 5b616af..308f754 100644 (file)
@@ -201,7 +201,8 @@ class Genesis BASE_EMBEDDED {
   // 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);
@@ -871,13 +872,21 @@ void Genesis::HookUpGlobalProxy(Handle<GlobalObject> global_object,
 }
 
 
-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(),
@@ -2724,9 +2733,10 @@ Genesis::Genesis(Isolate* isolate,
 
   // 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>();
   }
 
@@ -2748,7 +2758,7 @@ Genesis::Genesis(Isolate* isolate,
         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());
 
index 86348ae..a01c8b8 100644 (file)
@@ -635,6 +635,11 @@ void Deserializer::FlushICacheForNewCodeObjects() {
 
 
 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;
@@ -643,16 +648,22 @@ bool Deserializer::ReserveSpace() {
 }
 
 
-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();
@@ -688,33 +699,46 @@ void Deserializer::Deserialize(Isolate* isolate) {
 }
 
 
-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));
+  }
 }
 
 
@@ -1371,12 +1395,41 @@ void StartupSerializer::VisitPointers(Object** start, Object** end) {
 }
 
 
-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]
@@ -1633,6 +1686,15 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
   // 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);
+  }
 }
 
 
@@ -2276,52 +2338,47 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
   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() ||
@@ -2335,7 +2392,7 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
                                        *result, NULL, name);
   }
 
-  return result;
+  return scope.CloseAndEscape(result);
 }
 
 
index 5dc7374..499acde 100644 (file)
@@ -518,12 +518,12 @@ class Deserializer: public SerializerDeserializer {
   // 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();
 
@@ -542,6 +542,8 @@ class Deserializer: public SerializerDeserializer {
     UNREACHABLE();
   }
 
+  void Initialize(Isolate* isolate);
+
   void DecodeReservation(Vector<const SerializedData::Reservation> res);
 
   bool ReserveSpace();
@@ -738,11 +740,12 @@ class Serializer : public SerializerDeserializer {
 
 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();
   }
 
@@ -766,8 +769,11 @@ class PartialSerializer : public Serializer {
                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);
 };
 
index e38872d..09cf722 100644 (file)
@@ -38,7 +38,8 @@ bool Snapshot::Initialize(Isolate* isolate) {
 }
 
 
-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();
@@ -47,15 +48,18 @@ Handle<Context> Snapshot::NewContextFromSnapshot(Isolate* isolate) {
   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);
 }
 
 
index 25a07cd..04c7ba6 100644 (file)
@@ -16,7 +16,8 @@ class Snapshot : public AllStatic {
   // 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();
 
index acd904f..75074be 100644 (file)
@@ -391,24 +391,26 @@ UNINITIALIZED_DEPENDENT_TEST(PartialDeserialization, PartialSerialization) {
       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();
@@ -501,24 +503,135 @@ UNINITIALIZED_DEPENDENT_TEST(ContextDeserialization, ContextSerialization) {
       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();