diff options
author | Anna Henningsen <anna.henningsen@mongodb.com> | 2023-01-24 21:35:52 +0100 |
---|---|---|
committer | Node.js GitHub Bot <github-bot@iojs.org> | 2023-02-03 20:48:24 +0000 |
commit | bfadee5e6843e4116be8d5ecfb8f142fc82853f1 (patch) | |
tree | f623300e698201c879635c8cb3d1126e4b23b5f7 /src/node_snapshotable.cc | |
parent | 02fad4f40a10320406f22cd38c4994bd5fed0289 (diff) | |
download | node-new-bfadee5e6843e4116be8d5ecfb8f142fc82853f1.tar.gz |
src: allow snapshotting from the embedder API
PR-URL: https://github.com/nodejs/node/pull/45888
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Diffstat (limited to 'src/node_snapshotable.cc')
-rw-r--r-- | src/node_snapshotable.cc | 171 |
1 files changed, 84 insertions, 87 deletions
diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index a9f729f80e..aa66eb3312 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -33,6 +33,7 @@ using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; using v8::Local; +using v8::MaybeLocal; using v8::Object; using v8::ObjectTemplate; using v8::ScriptCompiler; @@ -1101,38 +1102,15 @@ void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data, ExitCode SnapshotBuilder::Generate(SnapshotData* out, const std::vector<std::string> args, const std::vector<std::string> exec_args) { - const std::vector<intptr_t>& external_references = - CollectExternalReferences(); - Isolate* isolate = Isolate::Allocate(); - // Must be done before the SnapshotCreator creation so that the - // memory reducer can be initialized. - per_process::v8_platform.Platform()->RegisterIsolate(isolate, - uv_default_loop()); - - SnapshotCreator creator(isolate, external_references.data()); - - isolate->SetCaptureStackTraceForUncaughtExceptions( - true, 10, v8::StackTrace::StackTraceOptions::kDetailed); - - Environment* env = nullptr; - std::unique_ptr<NodeMainInstance> main_instance = - NodeMainInstance::Create(isolate, - uv_default_loop(), - per_process::v8_platform.Platform(), - args, - exec_args); - - // The cleanups should be done in case of an early exit due to errors. - auto cleanup = OnScopeLeave([&]() { - // Must be done while the snapshot creator isolate is entered i.e. the - // creator is still alive. The snapshot creator destructor will destroy - // the isolate. - if (env != nullptr) { - FreeEnvironment(env); - } - main_instance->Dispose(); - per_process::v8_platform.Platform()->UnregisterIsolate(isolate); - }); + std::vector<std::string> errors; + auto setup = CommonEnvironmentSetup::CreateForSnapshotting( + per_process::v8_platform.Platform(), &errors, args, exec_args); + if (!setup) { + for (const std::string& err : errors) + fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str()); + return ExitCode::kBootstrapFailure; + } + Isolate* isolate = setup->isolate(); // It's only possible to be kDefault in node_mksnapshot. SnapshotMetadata::Type snapshot_type = @@ -1151,6 +1129,51 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out, } }); + // Initialize the main instance context. + { + Context::Scope context_scope(setup->context()); + Environment* env = setup->env(); + + // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be + // loaded via LoadEnvironment() to execute process.argv[1] as the entry + // point (we currently only support this kind of entry point, but we + // could also explore snapshotting other kinds of execution modes + // in the future). + if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) { +#if HAVE_INSPECTOR + env->InitializeInspector({}); +#endif + if (LoadEnvironment(env, StartExecutionCallback{}).IsEmpty()) { + return ExitCode::kGenericUserError; + } + // FIXME(joyeecheung): right now running the loop in the snapshot + // builder seems to introduces inconsistencies in JS land that need to + // be synchronized again after snapshot restoration. + ExitCode exit_code = + SpinEventLoopInternal(env).FromMaybe(ExitCode::kGenericUserError); + if (exit_code != ExitCode::kNoFailure) { + return exit_code; + } + } + } + } + + return CreateSnapshot(out, setup.get(), static_cast<uint8_t>(snapshot_type)); +} + +ExitCode SnapshotBuilder::CreateSnapshot(SnapshotData* out, + CommonEnvironmentSetup* setup, + uint8_t snapshot_type_u8) { + SnapshotMetadata::Type snapshot_type = + static_cast<SnapshotMetadata::Type>(snapshot_type_u8); + Isolate* isolate = setup->isolate(); + Environment* env = setup->env(); + SnapshotCreator* creator = setup->snapshot_creator(); + + { + HandleScope scope(isolate); + Local<Context> main_context = setup->context(); + // The default context with only things created by V8. Local<Context> default_context = Context::New(isolate); @@ -1158,7 +1181,7 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out, Local<Context> vm_context; { Local<ObjectTemplate> global_template = - main_instance->isolate_data()->contextify_global_template(); + setup->isolate_data()->contextify_global_template(); CHECK(!global_template.IsEmpty()); if (!contextify::ContextifyContext::CreateV8Context( isolate, global_template, nullptr, nullptr) @@ -1176,63 +1199,17 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out, } ResetContextSettingsBeforeSnapshot(base_context); - Local<Context> main_context = NewContext(isolate); - if (main_context.IsEmpty()) { - return ExitCode::kBootstrapFailure; - } - // Initialize the main instance context. { Context::Scope context_scope(main_context); - // Create the environment. - // It's not guaranteed that a context that goes through - // v8_inspector::V8Inspector::contextCreated() is runtime-independent, - // so do not start the inspector on the main context when building - // the default snapshot. - uint64_t env_flags = EnvironmentFlags::kDefaultFlags | - EnvironmentFlags::kNoCreateInspector; - - env = CreateEnvironment(main_instance->isolate_data(), - main_context, - args, - exec_args, - static_cast<EnvironmentFlags::Flags>(env_flags)); - - // This already ran scripts in lib/internal/bootstrap/, if it fails return - if (env == nullptr) { - return ExitCode::kBootstrapFailure; - } - // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be - // loaded via LoadEnvironment() to execute process.argv[1] as the entry - // point (we currently only support this kind of entry point, but we - // could also explore snapshotting other kinds of execution modes - // in the future). - if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) { -#if HAVE_INSPECTOR - env->InitializeInspector({}); -#endif - if (LoadEnvironment(env, StartExecutionCallback{}).IsEmpty()) { - return ExitCode::kGenericUserError; - } - // FIXME(joyeecheung): right now running the loop in the snapshot - // builder seems to introduces inconsistencies in JS land that need to - // be synchronized again after snapshot restoration. - ExitCode exit_code = - SpinEventLoopInternal(env).FromMaybe(ExitCode::kGenericUserError); - if (exit_code != ExitCode::kNoFailure) { - return exit_code; - } - } - if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) { env->ForEachRealm([](Realm* realm) { realm->PrintInfoForSnapshot(); }); printf("Environment = %p\n", env); } // Serialize the native states - out->isolate_data_info = - main_instance->isolate_data()->Serialize(&creator); - out->env_info = env->Serialize(&creator); + out->isolate_data_info = setup->isolate_data()->Serialize(creator); + out->env_info = env->Serialize(creator); #ifdef NODE_USE_NODE_CODE_CACHE // Regenerate all the code cache. @@ -1255,19 +1232,19 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out, // Global handles to the contexts can't be disposed before the // blob is created. So initialize all the contexts before adding them. // TODO(joyeecheung): figure out how to remove this restriction. - creator.SetDefaultContext(default_context); - size_t index = creator.AddContext(vm_context); + creator->SetDefaultContext(default_context); + size_t index = creator->AddContext(vm_context); CHECK_EQ(index, SnapshotData::kNodeVMContextIndex); - index = creator.AddContext(base_context); + index = creator->AddContext(base_context); CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex); - index = creator.AddContext(main_context, - {SerializeNodeContextInternalFields, env}); + index = creator->AddContext(main_context, + {SerializeNodeContextInternalFields, env}); CHECK_EQ(index, SnapshotData::kNodeMainContextIndex); } // Must be out of HandleScope out->v8_snapshot_blob_data = - creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kKeep); + creator->CreateBlob(SnapshotCreator::FunctionCodeHandling::kKeep); // We must be able to rehash the blob when we restore it or otherwise // the hash seed would be fixed by V8, introducing a vulnerability. @@ -1468,6 +1445,23 @@ void SerializeSnapshotableObjects(Realm* realm, namespace mksnapshot { +void GetEmbedderEntryFunction(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + if (!env->embedder_mksnapshot_entry_point()) return; + MaybeLocal<Function> jsfn = + Function::New(isolate->GetCurrentContext(), + [](const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + Local<Value> require_fn = args[0]; + CHECK(require_fn->IsFunction()); + CHECK(env->embedder_mksnapshot_entry_point()); + env->embedder_mksnapshot_entry_point()( + {env->process_object(), require_fn.As<Function>()}); + }); + if (!jsfn.IsEmpty()) args.GetReturnValue().Set(jsfn.ToLocalChecked()); +} + void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) { CHECK(args[0]->IsString()); Local<String> filename = args[0].As<String>(); @@ -1521,6 +1515,8 @@ void Initialize(Local<Object> target, Local<Value> unused, Local<Context> context, void* priv) { + SetMethod( + context, target, "getEmbedderEntryFunction", GetEmbedderEntryFunction); SetMethod(context, target, "compileSerializeMain", CompileSerializeMain); SetMethod(context, target, "setSerializeCallback", SetSerializeCallback); SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback); @@ -1531,6 +1527,7 @@ void Initialize(Local<Object> target, } void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(GetEmbedderEntryFunction); registry->Register(CompileSerializeMain); registry->Register(SetSerializeCallback); registry->Register(SetDeserializeCallback); |