Commit bcab9eac authored by yangguo's avatar yangguo Committed by Commit bot

Update references to global object after deserializing context.

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}
parent c24220c0
......@@ -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());
......
......@@ -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);
}
......
......@@ -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);
};
......
......@@ -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);
}
......
......@@ -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();
......
......@@ -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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment