#ifdef NDEBUG #undef NDEBUG #endif #include #include "executable_wrapper.h" #include "node.h" #include // Note: This file is being referred to from doc/api/embedding.md, and excerpts // from it are included in the documentation. Try to keep these in sync. // Snapshot support is not part of the embedder API docs yet due to its // experimental nature, although it is of course documented in node.h. using node::CommonEnvironmentSetup; using node::Environment; using node::MultiIsolatePlatform; using v8::Context; using v8::HandleScope; using v8::Isolate; using v8::Locker; using v8::MaybeLocal; using v8::V8; using v8::Value; static int RunNodeInstance(MultiIsolatePlatform* platform, const std::vector& args, const std::vector& exec_args); NODE_MAIN(int argc, node::argv_type raw_argv[]) { char** argv = nullptr; node::FixupMain(argc, raw_argv, &argv); std::vector args(argv, argv + argc); std::shared_ptr result = node::InitializeOncePerProcess( args, { node::ProcessInitializationFlags::kNoInitializeV8, node::ProcessInitializationFlags::kNoInitializeNodeV8Platform, // This is used to test NODE_REPL_EXTERNAL_MODULE is disabled with // kDisableNodeOptionsEnv. If other tests need NODE_OPTIONS // support in the future, split this configuration out as a // command line option. node::ProcessInitializationFlags::kDisableNodeOptionsEnv, }); for (const std::string& error : result->errors()) fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str()); if (result->early_return() != 0) { return result->exit_code(); } std::unique_ptr platform = MultiIsolatePlatform::Create(4); V8::InitializePlatform(platform.get()); V8::Initialize(); int ret = RunNodeInstance(platform.get(), result->args(), result->exec_args()); V8::Dispose(); V8::DisposePlatform(); node::TearDownOncePerProcess(); return ret; } int RunNodeInstance(MultiIsolatePlatform* platform, const std::vector& args, const std::vector& exec_args) { int exit_code = 0; // Format of the arguments of this binary: // Building snapshot: // embedtest js_code_to_eval arg1 arg2... // --embedder-snapshot-blob blob-path // --embedder-snapshot-create // [--embedder-snapshot-as-file] // [--without-code-cache] // Running snapshot: // embedtest --embedder-snapshot-blob blob-path // [--embedder-snapshot-as-file] // arg1 arg2... // No snapshot: // embedtest arg1 arg2... node::EmbedderSnapshotData::Pointer snapshot; std::string binary_path = args[0]; std::vector filtered_args; bool is_building_snapshot = false; bool snapshot_as_file = false; std::optional snapshot_config; std::string snapshot_blob_path; for (size_t i = 0; i < args.size(); ++i) { const std::string& arg = args[i]; if (arg == "--embedder-snapshot-create") { is_building_snapshot = true; } else if (arg == "--embedder-snapshot-as-file") { snapshot_as_file = true; } else if (arg == "--without-code-cache") { if (!snapshot_config.has_value()) { snapshot_config = node::SnapshotConfig{}; } snapshot_config.value().flags = static_cast( static_cast(snapshot_config.value().flags) | static_cast(node::SnapshotFlags::kWithoutCodeCache)); } else if (arg == "--embedder-snapshot-blob") { assert(i + 1 < args.size()); snapshot_blob_path = args[i + 1]; i++; } else { filtered_args.push_back(arg); } } if (!snapshot_blob_path.empty() && !is_building_snapshot) { FILE* fp = fopen(snapshot_blob_path.c_str(), "rb"); assert(fp != nullptr); if (snapshot_as_file) { snapshot = node::EmbedderSnapshotData::FromFile(fp); } else { uv_fs_t req = uv_fs_t(); int statret = uv_fs_stat(nullptr, &req, snapshot_blob_path.c_str(), nullptr); assert(statret == 0); size_t filesize = req.statbuf.st_size; uv_fs_req_cleanup(&req); std::vector vec(filesize); size_t read = fread(vec.data(), filesize, 1, fp); assert(read == 1); snapshot = node::EmbedderSnapshotData::FromBlob(vec); } assert(snapshot); int ret = fclose(fp); assert(ret == 0); } if (is_building_snapshot) { // It contains at least the binary path, the code to snapshot, // and --embedder-snapshot-create (which is filtered, so at least // 2 arguments should remain after filtering). assert(filtered_args.size() >= 2); // Insert an anonymous filename as process.argv[1]. filtered_args.insert(filtered_args.begin() + 1, node::GetAnonymousMainPath()); } std::vector errors; std::unique_ptr setup; if (snapshot) { setup = CommonEnvironmentSetup::CreateFromSnapshot( platform, &errors, snapshot.get(), filtered_args, exec_args); } else if (is_building_snapshot) { if (snapshot_config.has_value()) { setup = CommonEnvironmentSetup::CreateForSnapshotting( platform, &errors, filtered_args, exec_args, snapshot_config.value()); } else { setup = CommonEnvironmentSetup::CreateForSnapshotting( platform, &errors, filtered_args, exec_args); } } else { setup = CommonEnvironmentSetup::Create( platform, &errors, filtered_args, exec_args); } if (!setup) { for (const std::string& err : errors) fprintf(stderr, "%s: %s\n", binary_path.c_str(), err.c_str()); return 1; } Isolate* isolate = setup->isolate(); Environment* env = setup->env(); { Locker locker(isolate); Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); Context::Scope context_scope(setup->context()); MaybeLocal loadenv_ret; if (snapshot) { // Deserializing snapshot loadenv_ret = node::LoadEnvironment(env, node::StartExecutionCallback{}); } else if (is_building_snapshot) { // Environment created for snapshotting must set process.argv[1] to // the name of the main script, which was inserted above. loadenv_ret = node::LoadEnvironment( env, "const assert = require('assert');" "assert(require('v8').startupSnapshot.isBuildingSnapshot());" "globalThis.embedVars = { nön_ascıı: '🏳️‍🌈' };" "globalThis.require = require;" "require('vm').runInThisContext(process.argv[2]);"); } else { loadenv_ret = node::LoadEnvironment( env, "const publicRequire = require('module').createRequire(process.cwd() " "+ '/');" "globalThis.require = publicRequire;" "globalThis.embedVars = { nön_ascıı: '🏳️‍🌈' };" "require('vm').runInThisContext(process.argv[1]);"); } if (loadenv_ret.IsEmpty()) // There has been a JS exception. return 1; exit_code = node::SpinEventLoop(env).FromMaybe(1); } if (!snapshot_blob_path.empty() && is_building_snapshot) { snapshot = setup->CreateSnapshot(); assert(snapshot); FILE* fp = fopen(snapshot_blob_path.c_str(), "wb"); assert(fp != nullptr); if (snapshot_as_file) { snapshot->ToFile(fp); } else { const std::vector vec = snapshot->ToBlob(); size_t written = fwrite(vec.data(), vec.size(), 1, fp); assert(written == 1); } int ret = fclose(fp); assert(ret == 0); } node::Stop(env); return exit_code; }