[snapshot] Simplify deserializer APIs

This moves deserializer setup boilerplate inside the deserializers, and
makes improper usage less likely. For instance:

  ObjectDeserializer deserializer(&scd);
  /* ... deserializer setup ... */
  MaybeHandle<HeapObject> obj = deserializer.Deserialize(isolate);
  /* ... result checks and casts ... */

has now become:

  /* All setup and casts inside deserializer, impossible to illegally
     use the same instance multiple times. */
  MaybeHandle<SharedFunctionInfo> maybe_result =
      ObjectDeserializer::DeserializeSharedFunctionInfo(
          isolate, &scd, source);

Bug: v8:6624
Change-Id: Id5a1848e024e89cf86e5292389ba7c89f31d8e6b
Reviewed-on: https://chromium-review.googlesource.com/604791
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47219}
This commit is contained in:
Jakob Gruber 2017-08-08 13:19:19 +02:00 committed by Commit Bot
parent d929cc755c
commit 95f868595c
11 changed files with 139 additions and 189 deletions

View File

@ -2807,9 +2807,7 @@ bool Isolate::Init(StartupDeserializer* des) {
{
AlwaysAllocateScope always_allocate(this);
if (!create_heap_objects) {
des->Deserialize(this);
}
if (!create_heap_objects) des->DeserializeInto(this);
load_stub_cache_->Initialize();
store_stub_cache_->Initialize();
setup_delegate_->SetupInterpreter(interpreter_, create_heap_objects);

View File

@ -194,24 +194,17 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
return MaybeHandle<SharedFunctionInfo>();
}
ObjectDeserializer deserializer(&scd);
deserializer.AddAttachedObject(source);
Vector<const uint32_t> code_stub_keys = scd.CodeStubKeys();
for (int i = 0; i < code_stub_keys.length(); i++) {
deserializer.AddAttachedObject(
CodeStub::GetCode(isolate, code_stub_keys[i]).ToHandleChecked());
}
// Deserialize.
Handle<HeapObject> as_heap_object;
if (!deserializer.Deserialize(isolate).ToHandle(&as_heap_object)) {
MaybeHandle<SharedFunctionInfo> maybe_result =
ObjectDeserializer::DeserializeSharedFunctionInfo(isolate, &scd, source);
Handle<SharedFunctionInfo> result;
if (!maybe_result.ToHandle(&result)) {
// Deserializing may fail if the reservations cannot be fulfilled.
if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n");
return MaybeHandle<SharedFunctionInfo>();
}
Handle<SharedFunctionInfo> result =
Handle<SharedFunctionInfo>::cast(as_heap_object);
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int length = cached_data->length();
@ -265,34 +258,16 @@ MaybeHandle<FixedArray> WasmCompiledModuleSerializer::DeserializeWasmModule(
return nothing;
}
ObjectDeserializer deserializer(&scd);
deserializer.AddAttachedObject(isolate->native_context());
MaybeHandle<WasmCompiledModule> maybe_result =
ObjectDeserializer::DeserializeWasmCompiledModule(isolate, &scd,
wire_bytes);
MaybeHandle<String> maybe_wire_bytes_as_string =
isolate->factory()->NewStringFromOneByte(wire_bytes, TENURED);
Handle<String> wire_bytes_as_string;
if (!maybe_wire_bytes_as_string.ToHandle(&wire_bytes_as_string)) {
return nothing;
}
deserializer.AddAttachedObject(
handle(SeqOneByteString::cast(*wire_bytes_as_string)));
Handle<WasmCompiledModule> result;
if (!maybe_result.ToHandle(&result)) return nothing;
Vector<const uint32_t> stub_keys = scd.CodeStubKeys();
for (int i = 0; i < stub_keys.length(); ++i) {
deserializer.AddAttachedObject(
CodeStub::GetCode(isolate, stub_keys[i]).ToHandleChecked());
}
MaybeHandle<HeapObject> obj = deserializer.Deserialize(isolate);
if (obj.is_null() || !obj.ToHandleChecked()->IsFixedArray()) return nothing;
// Cast without type checks, as the module wrapper is not there yet.
Handle<WasmCompiledModule> compiled_module(
static_cast<WasmCompiledModule*>(*obj.ToHandleChecked()), isolate);
WasmCompiledModule::ReinitializeAfterDeserialization(isolate,
compiled_module);
DCHECK(WasmCompiledModule::IsWasmCompiledModule(*compiled_module));
return compiled_module;
WasmCompiledModule::ReinitializeAfterDeserialization(isolate, result);
DCHECK(WasmCompiledModule::IsWasmCompiledModule(*result));
return result;
}
void WasmCompiledModuleSerializer::SerializeCodeObject(

View File

@ -5,13 +5,66 @@
#include "src/snapshot/object-deserializer.h"
#include "src/assembler-inl.h"
#include "src/code-stubs.h"
#include "src/isolate.h"
#include "src/objects.h"
#include "src/snapshot/code-serializer.h"
#include "src/wasm/wasm-objects.h"
namespace v8 {
namespace internal {
MaybeHandle<SharedFunctionInfo>
ObjectDeserializer::DeserializeSharedFunctionInfo(
Isolate* isolate, const SerializedCodeData* data, Handle<String> source) {
ObjectDeserializer d(data);
d.AddAttachedObject(source);
Vector<const uint32_t> code_stub_keys = data->CodeStubKeys();
for (int i = 0; i < code_stub_keys.length(); i++) {
d.AddAttachedObject(
CodeStub::GetCode(isolate, code_stub_keys[i]).ToHandleChecked());
}
Handle<HeapObject> result;
return d.Deserialize(isolate).ToHandle(&result)
? Handle<SharedFunctionInfo>::cast(result)
: MaybeHandle<SharedFunctionInfo>();
}
MaybeHandle<WasmCompiledModule>
ObjectDeserializer::DeserializeWasmCompiledModule(
Isolate* isolate, const SerializedCodeData* data,
Vector<const byte> wire_bytes) {
ObjectDeserializer d(data);
d.AddAttachedObject(isolate->native_context());
MaybeHandle<String> maybe_wire_bytes_as_string =
isolate->factory()->NewStringFromOneByte(wire_bytes, TENURED);
Handle<String> wire_bytes_as_string;
if (!maybe_wire_bytes_as_string.ToHandle(&wire_bytes_as_string)) {
return MaybeHandle<WasmCompiledModule>();
}
d.AddAttachedObject(wire_bytes_as_string);
Vector<const uint32_t> code_stub_keys = data->CodeStubKeys();
for (int i = 0; i < code_stub_keys.length(); i++) {
d.AddAttachedObject(
CodeStub::GetCode(isolate, code_stub_keys[i]).ToHandleChecked());
}
Handle<HeapObject> result;
if (!d.Deserialize(isolate).ToHandle(&result))
return MaybeHandle<WasmCompiledModule>();
if (!result->IsFixedArray()) return MaybeHandle<WasmCompiledModule>();
// Cast without type checks, as the module wrapper is not there yet.
return handle(static_cast<WasmCompiledModule*>(*result), isolate);
}
MaybeHandle<HeapObject> ObjectDeserializer::Deserialize(Isolate* isolate) {
Initialize(isolate);
if (!ReserveSpace()) return MaybeHandle<HeapObject>();

View File

@ -11,19 +11,26 @@ namespace v8 {
namespace internal {
class SerializedCodeData;
class SharedFunctionInfo;
class WasmCompiledModule;
// Deserializes the object graph rooted at a given object.
// Currently, the ObjectDeserializer is only used to deserialize code objects
// and compiled wasm modules.
class ObjectDeserializer final : public Deserializer {
public:
static MaybeHandle<SharedFunctionInfo> DeserializeSharedFunctionInfo(
Isolate* isolate, const SerializedCodeData* data, Handle<String> source);
static MaybeHandle<WasmCompiledModule> DeserializeWasmCompiledModule(
Isolate* isolate, const SerializedCodeData* data,
Vector<const byte> wire_bytes);
private:
explicit ObjectDeserializer(const SerializedCodeData* data)
: Deserializer(data, true) {}
// Deserialize an object graph. Fail gracefully.
MaybeHandle<HeapObject> Deserialize(Isolate* isolate);
private:
void FlushICacheForNewCodeObjectsAndRecordEmbeddedObjects();
void CommitPostProcessedObjects();
};

View File

@ -10,6 +10,21 @@
namespace v8 {
namespace internal {
MaybeHandle<Context> PartialDeserializer::DeserializeContext(
Isolate* isolate, const SnapshotData* data, bool can_rehash,
Handle<JSGlobalProxy> global_proxy,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer) {
PartialDeserializer d(data);
d.SetRehashability(can_rehash);
MaybeHandle<Object> maybe_result =
d.Deserialize(isolate, global_proxy, embedder_fields_deserializer);
Handle<Object> result;
return maybe_result.ToHandle(&result) ? Handle<Context>::cast(result)
: MaybeHandle<Context>();
}
MaybeHandle<Object> PartialDeserializer::Deserialize(
Isolate* isolate, Handle<JSGlobalProxy> global_proxy,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer) {

View File

@ -11,11 +11,18 @@
namespace v8 {
namespace internal {
class Context;
// Deserializes the context-dependent object graph rooted at a given object.
// Currently, the only use-case is to deserialize native contexts.
// The PartialDeserializer is not expected to deserialize any code objects.
class PartialDeserializer final : public Deserializer {
public:
static MaybeHandle<Context> DeserializeContext(
Isolate* isolate, const SnapshotData* data, bool can_rehash,
Handle<JSGlobalProxy> global_proxy,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer);
private:
explicit PartialDeserializer(const SnapshotData* data)
: Deserializer(data, false) {}
@ -24,7 +31,6 @@ class PartialDeserializer final : public Deserializer {
Isolate* isolate, Handle<JSGlobalProxy> global_proxy,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer);
private:
void DeserializeEmbedderFields(
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer);

View File

@ -26,27 +26,25 @@ PartialSerializer::~PartialSerializer() {
}
void PartialSerializer::Serialize(Object** o, bool include_global_proxy) {
if ((*o)->IsNativeContext()) {
Context* context = Context::cast(*o);
reference_map()->AddAttachedReference(context->global_proxy());
// The bootstrap snapshot has a code-stub context. When serializing the
// partial snapshot, it is chained into the weak context list on the isolate
// and it's next context pointer may point to the code-stub context. Clear
// it before serializing, it will get re-added to the context list
// explicitly when it's loaded.
context->set(Context::NEXT_CONTEXT_LINK,
isolate_->heap()->undefined_value());
DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
// Reset math random cache to get fresh random numbers.
context->set_math_random_index(Smi::kZero);
context->set_math_random_cache(isolate_->heap()->undefined_value());
DCHECK_NULL(rehashable_global_dictionary_);
rehashable_global_dictionary_ =
context->global_object()->global_dictionary();
} else {
// We only do rehashing for native contexts.
can_be_rehashed_ = false;
}
DCHECK((*o)->IsNativeContext());
Context* context = Context::cast(*o);
reference_map()->AddAttachedReference(context->global_proxy());
// The bootstrap snapshot has a code-stub context. When serializing the
// partial snapshot, it is chained into the weak context list on the isolate
// and it's next context pointer may point to the code-stub context. Clear
// it before serializing, it will get re-added to the context list
// explicitly when it's loaded.
context->set(Context::NEXT_CONTEXT_LINK,
isolate_->heap()->undefined_value());
DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
// Reset math random cache to get fresh random numbers.
context->set_math_random_index(Smi::kZero);
context->set_math_random_cache(isolate_->heap()->undefined_value());
DCHECK_NULL(rehashable_global_dictionary_);
rehashable_global_dictionary_ =
context->global_object()->global_dictionary();
VisitRootPointer(Root::kPartialSnapshotCache, o);
SerializeDeferredObjects();
SerializeEmbedderFields();

View File

@ -60,24 +60,25 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
if (FLAG_profile_deserialization) timer.Start();
const v8::StartupData* blob = isolate->snapshot_blob();
bool can_rehash = ExtractRehashability(blob);
Vector<const byte> context_data =
ExtractContextData(blob, static_cast<int>(context_index));
SnapshotData snapshot_data(context_data);
PartialDeserializer deserializer(&snapshot_data);
deserializer.SetRehashability(ExtractRehashability(blob));
MaybeHandle<Object> maybe_context = deserializer.Deserialize(
isolate, global_proxy, embedder_fields_deserializer);
Handle<Object> result;
if (!maybe_context.ToHandle(&result)) return MaybeHandle<Context>();
CHECK(result->IsContext());
MaybeHandle<Context> maybe_result = PartialDeserializer::DeserializeContext(
isolate, &snapshot_data, can_rehash, global_proxy,
embedder_fields_deserializer);
Handle<Context> result;
if (!maybe_result.ToHandle(&result)) return MaybeHandle<Context>();
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int bytes = context_data.length();
PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
context_index, bytes, ms);
}
return Handle<Context>::cast(result);
return result;
}
void ProfileDeserialization(const SnapshotData* startup_snapshot,

View File

@ -11,7 +11,7 @@
namespace v8 {
namespace internal {
void StartupDeserializer::Deserialize(Isolate* isolate) {
void StartupDeserializer::DeserializeInto(Isolate* isolate) {
Initialize(isolate);
if (!ReserveSpace()) V8::FatalProcessOutOfMemory("StartupDeserializer");

View File

@ -18,7 +18,7 @@ class StartupDeserializer final : public Deserializer {
: Deserializer(data, false) {}
// Deserialize the snapshot into an empty heap.
void Deserialize(Isolate* isolate);
void DeserializeInto(Isolate* isolate);
private:
void FlushICacheForNewIsolate();

View File

@ -302,106 +302,6 @@ UNINITIALIZED_TEST(StartupSerializerTwiceRunScript) {
isolate->Dispose();
}
static void PartiallySerializeObject(Vector<const byte>* startup_blob_out,
Vector<const byte>* partial_blob_out) {
v8::Isolate* v8_isolate = TestIsolate::NewInitialized(true);
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
v8_isolate->Enter();
{
Heap* heap = isolate->heap();
v8::Persistent<v8::Context> env;
{
HandleScope scope(isolate);
env.Reset(v8_isolate, v8::Context::New(v8_isolate));
}
CHECK(!env.IsEmpty());
{
v8::HandleScope handle_scope(v8_isolate);
v8::Local<v8::Context>::New(v8_isolate, env)->Enter();
}
heap->CollectAllAvailableGarbage(i::GarbageCollectionReason::kTesting);
heap->CollectAllAvailableGarbage(i::GarbageCollectionReason::kTesting);
Object* raw_foo;
{
v8::HandleScope handle_scope(v8_isolate);
v8::Local<v8::String> foo = v8_str("foo");
CHECK(!foo.IsEmpty());
raw_foo = *(v8::Utils::OpenHandle(*foo));
}
{
v8::HandleScope handle_scope(v8_isolate);
v8::Local<v8::Context>::New(v8_isolate, env)->Exit();
}
env.Reset();
StartupSerializer startup_serializer(
isolate, v8::SnapshotCreator::FunctionCodeHandling::kClear);
startup_serializer.SerializeStrongReferences();
PartialSerializer partial_serializer(isolate, &startup_serializer,
v8::SerializeInternalFieldsCallback());
partial_serializer.Serialize(&raw_foo, false);
startup_serializer.SerializeWeakReferencesAndDeferred();
SnapshotData startup_snapshot(&startup_serializer);
SnapshotData partial_snapshot(&partial_serializer);
*partial_blob_out = WritePayload(partial_snapshot.RawData());
*startup_blob_out = WritePayload(startup_snapshot.RawData());
}
v8_isolate->Exit();
v8_isolate->Dispose();
}
UNINITIALIZED_TEST(PartialSerializerObject) {
DisableAlwaysOpt();
Vector<const byte> startup_blob;
Vector<const byte> partial_blob;
PartiallySerializeObject(&startup_blob, &partial_blob);
v8::Isolate* v8_isolate = InitializeFromBlob(startup_blob);
startup_blob.Dispose();
CHECK(v8_isolate);
{
v8::Isolate::Scope isolate_scope(v8_isolate);
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
HandleScope handle_scope(isolate);
Handle<Object> root;
// Intentionally empty handle. The deserializer should not come across
// any references to the global proxy in this test.
Handle<JSGlobalProxy> global_proxy = Handle<JSGlobalProxy>::null();
{
SnapshotData snapshot_data(partial_blob);
PartialDeserializer deserializer(&snapshot_data);
root = deserializer
.Deserialize(isolate, global_proxy,
v8::DeserializeInternalFieldsCallback())
.ToHandleChecked();
CHECK(root->IsString());
}
Handle<Object> root2;
{
SnapshotData snapshot_data(partial_blob);
PartialDeserializer deserializer(&snapshot_data);
root2 = deserializer
.Deserialize(isolate, global_proxy,
v8::DeserializeInternalFieldsCallback())
.ToHandleChecked();
CHECK(root2->IsString());
CHECK(root.is_identical_to(root2));
}
partial_blob.Dispose();
}
v8_isolate->Dispose();
}
static void PartiallySerializeContext(Vector<const byte>* startup_blob_out,
Vector<const byte>* partial_blob_out) {
v8::Isolate* v8_isolate = TestIsolate::NewInitialized(true);
@ -474,10 +374,9 @@ UNINITIALIZED_TEST(PartialSerializerContext) {
JSGlobalProxy::SizeWithEmbedderFields(0));
{
SnapshotData snapshot_data(partial_blob);
PartialDeserializer deserializer(&snapshot_data);
root = deserializer
.Deserialize(isolate, global_proxy,
v8::DeserializeInternalFieldsCallback())
root = PartialDeserializer::DeserializeContext(
isolate, &snapshot_data, false, global_proxy,
v8::DeserializeInternalFieldsCallback())
.ToHandleChecked();
CHECK(root->IsContext());
CHECK(Handle<Context>::cast(root)->global_proxy() == *global_proxy);
@ -486,10 +385,9 @@ UNINITIALIZED_TEST(PartialSerializerContext) {
Handle<Object> root2;
{
SnapshotData snapshot_data(partial_blob);
PartialDeserializer deserializer(&snapshot_data);
root2 = deserializer
.Deserialize(isolate, global_proxy,
v8::DeserializeInternalFieldsCallback())
root2 = PartialDeserializer::DeserializeContext(
isolate, &snapshot_data, false, global_proxy,
v8::DeserializeInternalFieldsCallback())
.ToHandleChecked();
CHECK(root2->IsContext());
CHECK(!root.is_identical_to(root2));
@ -592,10 +490,9 @@ UNINITIALIZED_TEST(PartialSerializerCustomContext) {
JSGlobalProxy::SizeWithEmbedderFields(0));
{
SnapshotData snapshot_data(partial_blob);
PartialDeserializer deserializer(&snapshot_data);
root = deserializer
.Deserialize(isolate, global_proxy,
v8::DeserializeInternalFieldsCallback())
root = PartialDeserializer::DeserializeContext(
isolate, &snapshot_data, false, global_proxy,
v8::DeserializeInternalFieldsCallback())
.ToHandleChecked();
CHECK(root->IsContext());
Handle<Context> context = Handle<Context>::cast(root);