Files
node/test/embedding/embedtest.cc
Joyee Cheung 718f62bfcf bootstrap: unify snapshot builder and embedder entry points
- Run the embedder entry point directly through
  runEmbedderEntryPoint(), instead of going through another
  JS -> C++ trip through the function returned by
  getEmbedderEntryFunction()
- For --build-snapshot, read the snapshot script code directly in C++
  and pass it to SnapshotBuilder::Generate(), this makes the entry point
  more explicit instead of hiding it in JS land, and also makes it
  possible to invoke SnapshotBuilder::Generate() internally to create
  a custom snapshot.
- Previously we used process.execPath for the embedder to create
  __filename and __dirname in the snapshot builder script while using
  process.argv[1] for --build-snapshot (where it's always set) which
  results in inconsistencies. We now require the embedder to also set
  args[1] when creating the Environment if they intend to run snapshot
  scripts with a context that contains __filename and __dirname, which
  would be derived from args[1]. If they prefer not to include
  build-time paths in the snapshot, we now provide
  node::GetAnonymousMainPath() as an alternative.

PR-URL: https://github.com/nodejs/node/pull/48242
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
2023-06-11 00:34:19 +02:00

198 lines
6.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifdef NDEBUG
#undef NDEBUG
#endif
#include "node.h"
#include "uv.h"
#include <assert.h>
#include <algorithm>
// 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<std::string>& args,
const std::vector<std::string>& exec_args);
int main(int argc, char** argv) {
argv = uv_setup_args(argc, argv);
std::vector<std::string> args(argv, argv + argc);
std::unique_ptr<node::InitializationResult> result =
node::InitializeOncePerProcess(
args,
{node::ProcessInitializationFlags::kNoInitializeV8,
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform});
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<MultiIsolatePlatform> 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<std::string>& args,
const std::vector<std::string>& 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]
// 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<std::string> filtered_args;
bool is_building_snapshot = false;
bool snapshot_as_file = false;
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 == "--embedder-snapshot-blob") {
assert(i + 1 < args.size());
snapshot_blob_path = args[i + i];
i++;
} else {
filtered_args.push_back(arg);
}
}
if (!snapshot_blob_path.empty() && !is_building_snapshot) {
FILE* fp = fopen(snapshot_blob_path.c_str(), "r");
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<char> 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. Insert an anonymous filename
// as process.argv[1].
assert(filtered_args.size() >= 3);
filtered_args.insert(filtered_args.begin() + 1,
node::GetAnonymousMainPath());
}
std::vector<std::string> errors;
std::unique_ptr<CommonEnvironmentSetup> setup =
snapshot
? CommonEnvironmentSetup::CreateFromSnapshot(
platform, &errors, snapshot.get(), filtered_args, exec_args)
: is_building_snapshot ? CommonEnvironmentSetup::CreateForSnapshotting(
platform, &errors, filtered_args, exec_args)
: 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<Value> loadenv_ret;
if (snapshot) {
loadenv_ret = node::LoadEnvironment(env, node::StartExecutionCallback{});
} else {
loadenv_ret = node::LoadEnvironment(
env,
// Snapshots do not support userland require()s (yet)
"if (!require('v8').startupSnapshot.isBuildingSnapshot()) {"
" const publicRequire ="
" require('module').createRequire(process.cwd() + '/');"
" globalThis.require = publicRequire;"
"} else globalThis.require = require;"
"globalThis.embedVars = { nön_ascıı: '🏳️‍🌈' };"
"require('vm').runInThisContext(process.argv[2]);");
}
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(), "w");
assert(fp != nullptr);
if (snapshot_as_file) {
snapshot->ToFile(fp);
} else {
const std::vector<char> 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;
}