summaryrefslogtreecommitdiff
path: root/src/node_snapshotable.cc
diff options
context:
space:
mode:
authorAnna Henningsen <anna.henningsen@mongodb.com>2023-01-24 21:35:52 +0100
committerNode.js GitHub Bot <github-bot@iojs.org>2023-02-03 20:48:24 +0000
commitbfadee5e6843e4116be8d5ecfb8f142fc82853f1 (patch)
treef623300e698201c879635c8cb3d1126e4b23b5f7 /src/node_snapshotable.cc
parent02fad4f40a10320406f22cd38c4994bd5fed0289 (diff)
downloadnode-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.cc171
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);