Commit 0b177bc0 authored by yangguo's avatar yangguo Committed by Commit bot

[snapshot] serialize embedder-provided external references.

R=jochen@chromium.org, vogelheim@chromium.org
BUG=chromium:617892

Review-Url: https://codereview.chromium.org/2066993004
Cr-Commit-Position: refs/heads/master@{#37109}
parent d800a659
......@@ -5499,13 +5499,14 @@ class V8_EXPORT Isolate {
*/
struct CreateParams {
CreateParams()
: entry_hook(NULL),
code_event_handler(NULL),
snapshot_blob(NULL),
counter_lookup_callback(NULL),
create_histogram_callback(NULL),
add_histogram_sample_callback(NULL),
array_buffer_allocator(NULL) {}
: entry_hook(nullptr),
code_event_handler(nullptr),
snapshot_blob(nullptr),
counter_lookup_callback(nullptr),
create_histogram_callback(nullptr),
add_histogram_sample_callback(nullptr),
array_buffer_allocator(nullptr),
external_references(nullptr) {}
/**
* The optional entry_hook allows the host application to provide the
......@@ -5553,6 +5554,14 @@ class V8_EXPORT Isolate {
* store of ArrayBuffers.
*/
ArrayBuffer::Allocator* array_buffer_allocator;
/**
* Specifies an optional nullptr-terminated array of raw addresses in the
* embedder that V8 can match against during serialization and use for
* deserialization. This array and its content must stay valid for the
* entire lifetime of the isolate.
*/
intptr_t* external_references;
};
......@@ -6781,8 +6790,12 @@ class SnapshotCreator {
* Create and enter an isolate, and set it up for serialization.
* The isolate is either created from scratch or from an existing snapshot.
* The caller keeps ownership of the argument snapshot.
* \param existing_blob existing snapshot from which to create this one.
* \param external_references a null-terminated array of external references
* that must be equivalent to CreateParams::external_references.
*/
explicit SnapshotCreator(StartupData* existing_blob = nullptr);
SnapshotCreator(intptr_t* external_references = nullptr,
StartupData* existing_blob = nullptr);
~SnapshotCreator();
......@@ -6799,6 +6812,7 @@ class SnapshotCreator {
/**
* Created a snapshot data blob.
* This must not be called from within a handle scope.
* \param function_code_handling whether to include compiled function code
* in the snapshot.
* \returns { nullptr, 0 } on failure, and a startup snapshot on success. The
......
......@@ -400,12 +400,14 @@ struct SnapshotCreatorData {
} // namespace
SnapshotCreator::SnapshotCreator(StartupData* existing_snapshot) {
SnapshotCreator::SnapshotCreator(intptr_t* external_references,
StartupData* existing_snapshot) {
i::Isolate* internal_isolate = new i::Isolate(true);
Isolate* isolate = reinterpret_cast<Isolate*>(internal_isolate);
SnapshotCreatorData* data = new SnapshotCreatorData(isolate);
data->isolate_ = isolate;
internal_isolate->set_array_buffer_allocator(&data->allocator_);
internal_isolate->set_api_external_references(external_references);
isolate->Enter();
if (existing_snapshot) {
internal_isolate->set_snapshot_blob(existing_snapshot);
......@@ -531,7 +533,7 @@ StartupData V8::WarmUpSnapshotDataBlob(StartupData cold_snapshot_blob,
base::ElapsedTimer timer;
timer.Start();
{
SnapshotCreator snapshot_creator(&cold_snapshot_blob);
SnapshotCreator snapshot_creator(nullptr, &cold_snapshot_blob);
Isolate* isolate = snapshot_creator.GetIsolate();
{
HandleScope scope(isolate);
......@@ -1138,8 +1140,7 @@ static Local<FunctionTemplate> FunctionTemplateNew(
obj->set_do_not_cache(do_not_cache);
int next_serial_number = 0;
if (!do_not_cache) {
next_serial_number = isolate->next_serial_number() + 1;
isolate->set_next_serial_number(next_serial_number);
next_serial_number = isolate->heap()->GetNextTemplateSerialNumber();
}
obj->set_serial_number(i::Smi::FromInt(next_serial_number));
if (callback != 0) {
......@@ -1166,7 +1167,6 @@ Local<FunctionTemplate> FunctionTemplate::New(Isolate* isolate,
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
// Changes to the environment cannot be captured in the snapshot. Expect no
// function templates when the isolate is created for serialization.
DCHECK(!i_isolate->serializer_enabled());
LOG_API(i_isolate, FunctionTemplate, New);
ENTER_V8(i_isolate);
return FunctionTemplateNew(i_isolate, callback, nullptr, data, signature,
......@@ -1361,9 +1361,6 @@ Local<ObjectTemplate> ObjectTemplate::New() {
static Local<ObjectTemplate> ObjectTemplateNew(
i::Isolate* isolate, v8::Local<FunctionTemplate> constructor,
bool do_not_cache) {
// Changes to the environment cannot be captured in the snapshot. Expect no
// object templates when the isolate is created for serialization.
DCHECK(!isolate->serializer_enabled());
LOG_API(isolate, ObjectTemplate, New);
ENTER_V8(isolate);
i::Handle<i::Struct> struct_obj =
......@@ -1373,8 +1370,7 @@ static Local<ObjectTemplate> ObjectTemplateNew(
InitializeTemplate(obj, Consts::OBJECT_TEMPLATE);
int next_serial_number = 0;
if (!do_not_cache) {
next_serial_number = isolate->next_serial_number() + 1;
isolate->set_next_serial_number(next_serial_number);
next_serial_number = isolate->heap()->GetNextTemplateSerialNumber();
}
obj->set_serial_number(i::Smi::FromInt(next_serial_number));
if (!constructor.IsEmpty())
......@@ -7279,6 +7275,8 @@ Isolate* Isolate::New(const Isolate::CreateParams& params) {
v8_isolate->SetAddHistogramSampleFunction(
params.add_histogram_sample_callback);
}
isolate->set_api_external_references(params.external_references);
SetResourceConstraints(isolate, params.constraints);
// TODO(jochen): Once we got rid of Isolate::Current(), we can remove this.
Isolate::Scope isolate_scope(v8_isolate);
......
......@@ -378,6 +378,15 @@ ExternalReferenceTable::ExternalReferenceTable(Isolate* isolate) {
Deoptimizer::CALCULATE_ENTRY_ADDRESS);
Add(address, "lazy_deopt");
}
// Add external references provided by the embedder (a null-terminated array).
intptr_t* api_external_references = isolate->api_external_references();
if (api_external_references != nullptr) {
while (*api_external_references != 0) {
Add(reinterpret_cast<Address>(*api_external_references), "<embedder>");
api_external_references++;
}
}
}
} // namespace internal
......
......@@ -726,6 +726,12 @@ void Heap::SetInterpreterEntryReturnPCOffset(int pc_offset) {
set_interpreter_entry_return_pc_offset(Smi::FromInt(pc_offset));
}
int Heap::GetNextTemplateSerialNumber() {
int next_serial_number = next_template_serial_number()->value() + 1;
set_next_template_serial_number(Smi::FromInt(next_serial_number));
return next_serial_number;
}
AlwaysAllocateScope::AlwaysAllocateScope(Isolate* isolate)
: heap_(isolate->heap()) {
heap_->always_allocate_scope_count_.Increment(1);
......
......@@ -2870,6 +2870,7 @@ void Heap::CreateInitialObjects() {
// Handling of script id generation is in Heap::NextScriptId().
set_last_script_id(Smi::FromInt(v8::UnboundScript::kNoScriptId));
set_next_template_serial_number(Smi::FromInt(0));
// Allocate the empty script.
Handle<Script> script = factory->NewScript(factory->empty_string());
......
......@@ -204,6 +204,9 @@ using v8::MemoryPressureLevel;
V(Smi, stack_limit, StackLimit) \
V(Smi, real_stack_limit, RealStackLimit) \
V(Smi, last_script_id, LastScriptId) \
/* To distinguish the function templates, so that we can find them in the */ \
/* function cache of the native context. */ \
V(Smi, next_template_serial_number, NextTemplateSerialNumber) \
V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset) \
V(Smi, construct_stub_deopt_pc_offset, ConstructStubDeoptPCOffset) \
V(Smi, getter_stub_deopt_pc_offset, GetterStubDeoptPCOffset) \
......@@ -802,6 +805,7 @@ class Heap {
inline void SetGetterStubDeoptPCOffset(int pc_offset);
inline void SetSetterStubDeoptPCOffset(int pc_offset);
inline void SetInterpreterEntryReturnPCOffset(int pc_offset);
inline int GetNextTemplateSerialNumber();
// For post mortem debugging.
void RememberUnmappedPage(Address page, bool compacted);
......
......@@ -398,29 +398,28 @@ typedef List<HeapObject*> DebugObjectCache;
#define ISOLATE_INIT_LIST(V) \
/* Assembler state. */ \
V(FatalErrorCallback, exception_behavior, NULL) \
V(LogEventCallback, event_logger, NULL) \
V(AllowCodeGenerationFromStringsCallback, allow_code_gen_callback, NULL) \
/* To distinguish the function templates, so that we can find them in the */ \
/* function cache of the native context. */ \
V(int, next_serial_number, 0) \
V(ExternalReferenceRedirectorPointer*, external_reference_redirector, NULL) \
V(FatalErrorCallback, exception_behavior, nullptr) \
V(LogEventCallback, event_logger, nullptr) \
V(AllowCodeGenerationFromStringsCallback, allow_code_gen_callback, nullptr) \
V(ExternalReferenceRedirectorPointer*, external_reference_redirector, \
nullptr) \
/* State for Relocatable. */ \
V(Relocatable*, relocatable_top, NULL) \
V(DebugObjectCache*, string_stream_debug_object_cache, NULL) \
V(Object*, string_stream_current_security_token, NULL) \
V(ExternalReferenceTable*, external_reference_table, NULL) \
V(base::HashMap*, external_reference_map, NULL) \
V(base::HashMap*, root_index_map, NULL) \
V(Relocatable*, relocatable_top, nullptr) \
V(DebugObjectCache*, string_stream_debug_object_cache, nullptr) \
V(Object*, string_stream_current_security_token, nullptr) \
V(ExternalReferenceTable*, external_reference_table, nullptr) \
V(intptr_t*, api_external_references, nullptr) \
V(base::HashMap*, external_reference_map, nullptr) \
V(base::HashMap*, root_index_map, nullptr) \
V(int, pending_microtask_count, 0) \
V(HStatistics*, hstatistics, NULL) \
V(CompilationStatistics*, turbo_statistics, NULL) \
V(HTracer*, htracer, NULL) \
V(CodeTracer*, code_tracer, NULL) \
V(HStatistics*, hstatistics, nullptr) \
V(CompilationStatistics*, turbo_statistics, nullptr) \
V(HTracer*, htracer, nullptr) \
V(CodeTracer*, code_tracer, nullptr) \
V(bool, fp_stubs_generated, false) \
V(uint32_t, per_isolate_assert_data, 0xFFFFFFFFu) \
V(PromiseRejectCallback, promise_reject_callback, NULL) \
V(const v8::StartupData*, snapshot_blob, NULL) \
V(PromiseRejectCallback, promise_reject_callback, nullptr) \
V(const v8::StartupData*, snapshot_blob, nullptr) \
V(int, code_and_metadata_size, 0) \
V(int, bytecode_and_metadata_size, 0) \
/* true if being profiled. Causes collection of extra compile info. */ \
......
......@@ -1947,6 +1947,157 @@ TEST(SnapshotCreatorMultipleContexts) {
delete[] blob.data;
}
static void SerializedCallback(
const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(v8_num(42));
}
static void SerializedCallbackReplacement(
const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(v8_num(1337));
}
intptr_t original_external_references[] = {
reinterpret_cast<intptr_t>(SerializedCallback), 0};
intptr_t replaced_external_references[] = {
reinterpret_cast<intptr_t>(SerializedCallbackReplacement), 0};
TEST(SnapshotCreatorExternalReferences) {
DisableTurbofan();
v8::StartupData blob;
{
v8::SnapshotCreator creator(original_external_references);
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::Local<v8::FunctionTemplate> callback =
v8::FunctionTemplate::New(isolate, SerializedCallback);
v8::Local<v8::Value> function =
callback->GetFunction(context).ToLocalChecked();
CHECK(context->Global()->Set(context, v8_str("f"), function).FromJust());
ExpectInt32("f()", 42);
CHECK_EQ(0, creator.AddContext(context));
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
// Deserialize with the original external reference.
{
v8::Isolate::CreateParams params;
params.snapshot_blob = &blob;
params.array_buffer_allocator = CcTest::array_buffer_allocator();
params.external_references = original_external_references;
v8::Isolate* isolate = v8::Isolate::New(params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::ExtensionConfiguration* no_extension = nullptr;
v8::Local<v8::ObjectTemplate> no_template =
v8::Local<v8::ObjectTemplate>();
v8::Local<v8::Value> no_object = v8::Local<v8::Value>();
v8::Local<v8::Context> context =
v8::Context::New(isolate, no_extension, no_template, no_object, 0);
v8::Context::Scope context_scope(context);
ExpectInt32("f()", 42);
}
isolate->Dispose();
}
// Deserialize with the some other external reference.
{
v8::Isolate::CreateParams params;
params.snapshot_blob = &blob;
params.array_buffer_allocator = CcTest::array_buffer_allocator();
params.external_references = replaced_external_references;
v8::Isolate* isolate = v8::Isolate::New(params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::ExtensionConfiguration* no_extension = nullptr;
v8::Local<v8::ObjectTemplate> no_template =
v8::Local<v8::ObjectTemplate>();
v8::Local<v8::Value> no_object = v8::Local<v8::Value>();
v8::Local<v8::Context> context =
v8::Context::New(isolate, no_extension, no_template, no_object, 0);
v8::Context::Scope context_scope(context);
ExpectInt32("f()", 1337);
}
isolate->Dispose();
}
delete[] blob.data;
}
TEST(SnapshotCreatorGlobalTemplate) {
DisableTurbofan();
v8::StartupData blob;
{
v8::SnapshotCreator creator(original_external_references);
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::ExtensionConfiguration* no_extension = nullptr;
v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->Set(
v8_str("f"), v8::FunctionTemplate::New(isolate, SerializedCallback));
v8::Local<v8::Context> context =
v8::Context::New(isolate, no_extension, global_template);
v8::Context::Scope context_scope(context);
ExpectInt32("f()", 42);
CHECK_EQ(0, creator.AddContext(context));
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
{
v8::Isolate::CreateParams params;
params.snapshot_blob = &blob;
params.array_buffer_allocator = CcTest::array_buffer_allocator();
params.external_references = original_external_references;
v8::Isolate* isolate = v8::Isolate::New(params);
{
v8::Isolate::Scope isolate_scope(isolate);
{
// Create a new context without a new object template.
v8::HandleScope handle_scope(isolate);
v8::ExtensionConfiguration* no_extension = nullptr;
v8::Local<v8::ObjectTemplate> no_template =
v8::Local<v8::ObjectTemplate>();
v8::Local<v8::Value> no_object = v8::Local<v8::Value>();
v8::Local<v8::Context> context =
v8::Context::New(isolate, no_extension, no_template, no_object, 0);
v8::Context::Scope context_scope(context);
ExpectInt32("f()", 42);
}
{
// Create a context with a new object template. It is merged into the
// deserialized global object.
v8::HandleScope handle_scope(isolate);
v8::ExtensionConfiguration* no_extension = nullptr;
v8::Local<v8::ObjectTemplate> global_template =
v8::ObjectTemplate::New(isolate);
global_template->Set(
v8_str("g"),
v8::FunctionTemplate::New(isolate, SerializedCallbackReplacement));
v8::Local<v8::Value> no_object = v8::Local<v8::Value>();
v8::Local<v8::Context> context = v8::Context::New(
isolate, no_extension, global_template, no_object, 0);
v8::Context::Scope context_scope(context);
ExpectInt32("g()", 1337);
ExpectInt32("f()", 42);
}
}
isolate->Dispose();
}
delete[] blob.data;
}
TEST(SerializationMemoryStats) {
FLAG_profile_deserialization = true;
FLAG_always_opt = false;
......
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