From d2746afd3129f1d26f70a64f95c3c2c5e1efbfd2 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 24 Jan 2023 21:35:52 +0100 Subject: [PATCH] [WIP] allow snapshotting from the embedder API --- src/api/embed_helpers.cc | 91 +++++++++++++++--- src/node.h | 32 ++++++- src/node_main_instance.cc | 35 ------- src/node_main_instance.h | 27 ------ src/node_snapshot_builder.h | 9 +- src/node_snapshotable.cc | 153 ++++++++++++++----------------- test/embedding/embedtest.cc | 27 +++++- test/embedding/test-embedding.js | 13 ++- 8 files changed, 216 insertions(+), 171 deletions(-) diff --git a/src/api/embed_helpers.cc b/src/api/embed_helpers.cc index 3944890e443885..75f2068968e173 100644 --- a/src/api/embed_helpers.cc +++ b/src/api/embed_helpers.cc @@ -14,6 +14,7 @@ using v8::Locker; using v8::Maybe; using v8::Nothing; using v8::SealHandleScope; +using v8::SnapshotCreator; namespace node { @@ -78,16 +79,18 @@ struct CommonEnvironmentSetup::Impl { MultiIsolatePlatform* platform = nullptr; uv_loop_t loop; std::shared_ptr allocator; + std::optional snapshot_creator; Isolate* isolate = nullptr; DeleteFnPtr isolate_data; DeleteFnPtr env; - Global context; + Global main_context; }; CommonEnvironmentSetup::CommonEnvironmentSetup( MultiIsolatePlatform* platform, std::vector* errors, const EmbedderSnapshotData* snapshot_data, + bool is_snapshotting, std::function make_env) : impl_(new Impl()) { CHECK_NOT_NULL(platform); @@ -105,28 +108,42 @@ CommonEnvironmentSetup::CommonEnvironmentSetup( } loop->data = this; - impl_->allocator = ArrayBufferAllocator::Create(); - impl_->isolate = - NewIsolate(impl_->allocator, &impl_->loop, platform, snapshot_data); - Isolate* isolate = impl_->isolate; + Isolate* isolate; + if (is_snapshotting) { + const std::vector& external_references = + SnapshotBuilder::CollectExternalReferences(); + isolate = impl_->isolate = Isolate::Allocate(); + // Must be done before the SnapshotCreator creation so that the + // memory reducer can be initialized. + platform->RegisterIsolate(isolate, loop); + impl_->snapshot_creator.emplace(isolate, external_references.data()); + isolate->SetCaptureStackTraceForUncaughtExceptions( + true, 10, v8::StackTrace::StackTraceOptions::kDetailed); + SetIsolateMiscHandlers(isolate, {}); + } else { + impl_->allocator = ArrayBufferAllocator::Create(); + isolate = impl_->isolate = + NewIsolate(impl_->allocator, &impl_->loop, platform, snapshot_data); + } { Locker locker(isolate); Isolate::Scope isolate_scope(isolate); impl_->isolate_data.reset(CreateIsolateData( isolate, loop, platform, impl_->allocator.get(), snapshot_data)); + impl_->isolate_data->options()->build_snapshot = is_snapshotting; HandleScope handle_scope(isolate); if (snapshot_data) { impl_->env.reset(make_env(this)); if (impl_->env) { - impl_->context.Reset(isolate, impl_->env->context()); + impl_->main_context.Reset(isolate, impl_->env->context()); } return; } Local context = NewContext(isolate); - impl_->context.Reset(isolate, context); + impl_->main_context.Reset(isolate, context); if (context.IsEmpty()) { errors->push_back("Failed to initialize V8 Context"); return; @@ -141,7 +158,34 @@ CommonEnvironmentSetup::CommonEnvironmentSetup( MultiIsolatePlatform* platform, std::vector* errors, std::function make_env) - : CommonEnvironmentSetup(platform, errors, nullptr, make_env) {} + : CommonEnvironmentSetup(platform, errors, nullptr, false, make_env) {} + +std::unique_ptr +CommonEnvironmentSetup::CreateForSnapshotting( + MultiIsolatePlatform* platform, + std::vector* errors, + const std::vector& args, + const std::vector& exec_args) { + // 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; + + auto ret = std::unique_ptr(new CommonEnvironmentSetup( + platform, errors, nullptr, true, + [&](const CommonEnvironmentSetup* setup) -> Environment* { + return CreateEnvironment( + setup->isolate_data(), + setup->context(), + args, + exec_args, + static_cast(env_flags)); + })); + if (!errors->empty()) ret.reset(); + return ret; +} CommonEnvironmentSetup::~CommonEnvironmentSetup() { if (impl_->isolate != nullptr) { @@ -150,7 +194,7 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() { Locker locker(isolate); Isolate::Scope isolate_scope(isolate); - impl_->context.Reset(); + impl_->main_context.Reset(); impl_->env.reset(); impl_->isolate_data.reset(); } @@ -160,7 +204,10 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() { *static_cast(data) = true; }, &platform_finished); impl_->platform->UnregisterIsolate(isolate); - isolate->Dispose(); + if (impl_->snapshot_creator.has_value()) + impl_->snapshot_creator.reset(); + else + isolate->Dispose(); // Wait until the platform has cleaned up all relevant resources. while (!platform_finished) @@ -173,6 +220,20 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() { delete impl_; } +EmbedderSnapshotData::Pointer CommonEnvironmentSetup::CreateSnapshot() { + CHECK_NOT_NULL(snapshot_creator()); + SnapshotData* snapshot_data = new SnapshotData(); + EmbedderSnapshotData::Pointer result{ + new EmbedderSnapshotData(snapshot_data, true)}; + + auto exit_code = SnapshotBuilder::CreateSnapshot( + snapshot_data, this, + static_cast(SnapshotMetadata::Type::kFullyCustomized)); + if (exit_code != ExitCode::kNoFailure) return {}; + + return result; +} + Maybe SpinEventLoop(Environment* env) { Maybe result = SpinEventLoopInternal(env); if (result.IsNothing()) { @@ -203,7 +264,11 @@ Environment* CommonEnvironmentSetup::env() const { } v8::Local CommonEnvironmentSetup::context() const { - return impl_->context.Get(impl_->isolate); + return impl_->main_context.Get(impl_->isolate); +} + +v8::SnapshotCreator* CommonEnvironmentSetup::snapshot_creator() { + return impl_->snapshot_creator ? &impl_->snapshot_creator.value() : nullptr; } void EmbedderSnapshotData::DeleteSnapshotData::operator()( @@ -232,6 +297,10 @@ EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) { return result; } +void EmbedderSnapshotData::ToFile(FILE* out) const { + impl_->ToBlob(out); +} + EmbedderSnapshotData::EmbedderSnapshotData(const SnapshotData* impl, bool owns_impl) : impl_(impl), owns_impl_(owns_impl) {} diff --git a/src/node.h b/src/node.h index ce570e5273836f..d05c2cf8682241 100644 --- a/src/node.h +++ b/src/node.h @@ -511,11 +511,16 @@ class EmbedderSnapshotData { static Pointer BuiltinSnapshotData(); // Return an EmbedderSnapshotData object that is based on an input file. - // Calling this method will not consume but not close the FILE* handle. + // Calling this method will consume but not close the FILE* handle. // The FILE* handle can be closed immediately following this call. // If the snapshot is invalid, this returns an empty pointer. static Pointer FromFile(FILE* in); + // Write this EmbedderSnapshotData object to an output file. + // Calling this method will not close the FILE* handle. + // The FILE* handle can be closed immediately following this call. + void ToFile(FILE* out) const; + // Returns whether custom snapshots can be used. Currently, this means // that V8 was configured without the shared-readonly-heap feature. static bool CanUseCustomSnapshotPerIsolate(); @@ -532,6 +537,7 @@ class EmbedderSnapshotData { const SnapshotData* impl_; bool owns_impl_; friend struct SnapshotData; + friend class CommonEnvironmentSetup; }; // Overriding IsolateSettings may produce unexpected behavior @@ -823,7 +829,29 @@ class NODE_EXTERN CommonEnvironmentSetup { const EmbedderSnapshotData* snapshot_data, EnvironmentArgs&&... env_args); + // Create an embedding setup which will be used for creating a snapshot + // using CreateSnapshot(). + // + // This will create and attach a v8::SnapshotCreator to this instance, + // and the same restrictions apply to this instance that also apply to + // other V8 snapshotting environments. + // Not all Node.js APIs are supported in this case. Currently, there is + // no support for native/host objects other than Node.js builtins + // in the snapshot. + // + // Snapshots are an *experimental* feature. In particular, the embedder API + // exposed through this class is subject to change or removal between Node.js + // versions, including possible API and ABI breakage. + static std::unique_ptr CreateForSnapshotting( + MultiIsolatePlatform* platform, + std::vector* errors, + const std::vector& args = {}, + const std::vector& exec_args = {}); + EmbedderSnapshotData::Pointer CreateSnapshot(); + struct uv_loop_s* event_loop() const; + v8::SnapshotCreator* snapshot_creator(); + // Empty for snapshotting environments. std::shared_ptr array_buffer_allocator() const; v8::Isolate* isolate() const; IsolateData* isolate_data() const; @@ -846,6 +874,7 @@ class NODE_EXTERN CommonEnvironmentSetup { MultiIsolatePlatform*, std::vector*, const EmbedderSnapshotData*, + bool is_snapshotting, std::function); }; @@ -878,6 +907,7 @@ CommonEnvironmentSetup::CreateWithSnapshot( platform, errors, snapshot_data, + false, [&](const CommonEnvironmentSetup* setup) -> Environment* { return CreateEnvironment(setup->isolate_data(), setup->context(), diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 81804656c51093..5a4d127ffe3e43 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -29,34 +29,6 @@ using v8::Isolate; using v8::Local; using v8::Locker; -NodeMainInstance::NodeMainInstance(Isolate* isolate, - uv_loop_t* event_loop, - MultiIsolatePlatform* platform, - const std::vector& args, - const std::vector& exec_args) - : args_(args), - exec_args_(exec_args), - array_buffer_allocator_(nullptr), - isolate_(isolate), - platform_(platform), - isolate_data_(nullptr), - snapshot_data_(nullptr) { - isolate_data_ = - std::make_unique(isolate_, event_loop, platform, nullptr); - - SetIsolateMiscHandlers(isolate_, {}); -} - -std::unique_ptr NodeMainInstance::Create( - Isolate* isolate, - uv_loop_t* event_loop, - MultiIsolatePlatform* platform, - const std::vector& args, - const std::vector& exec_args) { - return std::unique_ptr( - new NodeMainInstance(isolate, event_loop, platform, args, exec_args)); -} - NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data, uv_loop_t* event_loop, MultiIsolatePlatform* platform, @@ -88,13 +60,6 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data, isolate_params_->constraints.max_young_generation_size_in_bytes(); } -void NodeMainInstance::Dispose() { - // This should only be called on a main instance that does not own its - // isolate. - CHECK_NULL(isolate_params_); - platform_->DrainTasks(isolate_); -} - NodeMainInstance::~NodeMainInstance() { if (isolate_params_ == nullptr) { return; diff --git a/src/node_main_instance.h b/src/node_main_instance.h index 43b6a8d64e409f..2752ff4d0ebc07 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -22,33 +22,6 @@ struct SnapshotData; // We may be able to create an abstract class to reuse some of the routines. class NodeMainInstance { public: - // To create a main instance that does not own the isolate, - // The caller needs to do: - // - // Isolate* isolate = Isolate::Allocate(); - // platform->RegisterIsolate(isolate, loop); - // isolate->Initialize(...); - // isolate->Enter(); - // std::unique_ptr main_instance = - // NodeMainInstance::Create(isolate, loop, args, exec_args); - // - // When tearing it down: - // - // main_instance->Cleanup(); // While the isolate is entered - // isolate->Exit(); - // isolate->Dispose(); - // platform->UnregisterIsolate(isolate); - // - // After calling Dispose() the main_instance is no longer accessible. - static std::unique_ptr Create( - v8::Isolate* isolate, - uv_loop_t* event_loop, - MultiIsolatePlatform* platform, - const std::vector& args, - const std::vector& exec_args); - - void Dispose(); - // Create a main instance that owns the isolate NodeMainInstance(const SnapshotData* snapshot_data, uv_loop_t* event_loop, diff --git a/src/node_snapshot_builder.h b/src/node_snapshot_builder.h index 3367cd3d7a6c68..2a90d3d1b84b16 100644 --- a/src/node_snapshot_builder.h +++ b/src/node_snapshot_builder.h @@ -31,10 +31,15 @@ class NODE_EXTERN_PRIVATE SnapshotBuilder { static void InitializeIsolateParams(const SnapshotData* data, v8::Isolate::CreateParams* params); - private: static const std::vector& CollectExternalReferences(); - static std::unique_ptr registry_; + static ExitCode CreateSnapshot( + SnapshotData* out, + CommonEnvironmentSetup* setup, + /*SnapshotMetadata::Type*/uint8_t snapshot_type); + + private: + static std::unique_ptr registry_; }; } // namespace node diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index 49a1f45efacee5..77659f1a8e7c97 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -1081,38 +1081,18 @@ void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data, ExitCode SnapshotBuilder::Generate(SnapshotData* out, const std::vector args, const std::vector exec_args) { - const std::vector& 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 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 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 = @@ -1131,6 +1111,54 @@ 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(snapshot_type)); +} + +ExitCode +SnapshotBuilder::CreateSnapshot( + SnapshotData* out, + CommonEnvironmentSetup* setup, + uint8_t snapshot_type_u8) { + SnapshotMetadata::Type snapshot_type = + static_cast(snapshot_type_u8); + Isolate* isolate = setup->isolate(); + Environment* env = setup->env(); + SnapshotCreator* creator = setup->snapshot_creator(); + + { + HandleScope scope(isolate); + Local main_context = setup->context(); + // The default context with only things created by V8. Local default_context = Context::New(isolate); @@ -1138,7 +1166,7 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out, Local vm_context; { Local 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) @@ -1156,54 +1184,9 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out, } ResetContextSettingsBeforeSnapshot(base_context); - Local 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(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); @@ -1211,8 +1194,8 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out, // Serialize the native states out->isolate_data_info = - main_instance->isolate_data()->Serialize(&creator); - out->env_info = env->Serialize(&creator); + setup->isolate_data()->Serialize(creator); + out->env_info = env->Serialize(creator); #ifdef NODE_USE_NODE_CODE_CACHE // Regenerate all the code cache. @@ -1235,19 +1218,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, + 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. diff --git a/test/embedding/embedtest.cc b/test/embedding/embedtest.cc index ea3af217319ef7..b8e9d66a93cd9c 100644 --- a/test/embedding/embedtest.cc +++ b/test/embedding/embedtest.cc @@ -1,3 +1,6 @@ +#ifdef NDEBUG +#undef NDEBUG +#endif #include "node.h" #include "uv.h" #include @@ -56,11 +59,15 @@ int RunNodeInstance(MultiIsolatePlatform* platform, const std::vector& args, const std::vector& exec_args) { int exit_code = 0; + V8::SetFlagsFromString("--abort-on-uncaught-exception"); node::EmbedderSnapshotData::Pointer snapshot; + auto snapshot_build_mode_it = + std::find(args.begin(), args.end(), "--embedder-snapshot-create"); auto snapshot_arg_it = std::find(args.begin(), args.end(), "--embedder-snapshot-blob"); - if (snapshot_arg_it < args.end() - 1) { + if (snapshot_arg_it < args.end() - 1 && + snapshot_build_mode_it == args.end()) { FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "r"); assert(fp != nullptr); snapshot = node::EmbedderSnapshotData::FromFile(fp); @@ -71,7 +78,10 @@ int RunNodeInstance(MultiIsolatePlatform* platform, std::unique_ptr setup = snapshot ? CommonEnvironmentSetup::CreateWithSnapshot( - platform, &errors, snapshot.get(), args, exec_args) + platform, &errors, snapshot.get(), args, exec_args) : + snapshot_build_mode_it != args.end() + ? CommonEnvironmentSetup::CreateForSnapshotting( + platform, &errors, args, exec_args) : CommonEnvironmentSetup::Create(platform, &errors, args, exec_args); if (!setup) { for (const std::string& err : errors) @@ -105,9 +115,20 @@ int RunNodeInstance(MultiIsolatePlatform* platform, return 1; exit_code = node::SpinEventLoop(env).FromMaybe(1); + } + + if (snapshot_arg_it < args.end() - 1 && + snapshot_build_mode_it != args.end()) { + snapshot = setup->CreateSnapshot(); + assert(snapshot); - node::Stop(env); + FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "w"); + assert(fp != nullptr); + snapshot->ToFile(fp); + fclose(fp); } + node::Stop(env); + return exit_code; } diff --git a/test/embedding/test-embedding.js b/test/embedding/test-embedding.js index c21c39b28e99dd..f15c2cc4e9da36 100644 --- a/test/embedding/test-embedding.js +++ b/test/embedding/test-embedding.js @@ -20,7 +20,6 @@ function resolveBuiltBinary(bin) { } const binary = resolveBuiltBinary('embedtest'); -const standaloneNodeBinary = resolveBuiltBinary('node'); assert.strictEqual( child_process.spawnSync(binary, ['console.log(42)']) @@ -55,18 +54,18 @@ assert.strictEqual( { const snapshotFixture = fixtures.path('snapshot', 'echo-args.js'); const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob'); - const buildSnapshotArgs = [snapshotFixture, 'arg1', 'arg2']; + const buildSnapshotArgs = [`require(${JSON.stringify(snapshotFixture)})`, 'arg1', 'arg2']; const runEmbeddedArgs = ['--embedder-snapshot-blob', blobPath, 'arg3', 'arg4']; fs.rmSync(blobPath, { force: true }); - assert.strictEqual(child_process.spawnSync(standaloneNodeBinary, [ - '--snapshot-blob', blobPath, '--build-snapshot', ...buildSnapshotArgs, + assert.strictEqual(child_process.spawnSync(binary, [ + '--', ...buildSnapshotArgs, '--embedder-snapshot-blob', blobPath, '--embedder-snapshot-create', ], { cwd: tmpdir.path, }).status, 0); const spawnResult = child_process.spawnSync(binary, ['--', ...runEmbeddedArgs]); assert.deepStrictEqual(JSON.parse(spawnResult.stdout), { - originalArgv: [standaloneNodeBinary, ...buildSnapshotArgs], + originalArgv: [binary, ...buildSnapshotArgs], currentArgv: [binary, ...runEmbeddedArgs], }); } @@ -77,8 +76,8 @@ assert.strictEqual( const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob'); fs.rmSync(blobPath, { force: true }); - assert.strictEqual(child_process.spawnSync(standaloneNodeBinary, [ - '--snapshot-blob', blobPath, '--build-snapshot', snapshotFixture, + assert.strictEqual(child_process.spawnSync(binary, [ + '--', `require(${JSON.stringify(snapshotFixture)})`, '--embedder-snapshot-blob', blobPath, '--embedder-snapshot-create', ], { cwd: tmpdir.path, }).status, 0);