node/test/embedding/embedtest.cc
Rafael Gonzaga b9ad94b6da
src: fix external module env and kDisableNodeOptionsEnv
PR-URL: https://github.com/nodejs/node/pull/52905
Fixes: https://github.com/nodejs/node/issues/51227
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
2024-05-23 18:41:55 +00:00

233 lines
7.7 KiB
C++
Raw Permalink 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 <assert.h>
#include "executable_wrapper.h"
#include "node.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);
NODE_MAIN(int argc, node::argv_type raw_argv[]) {
char** argv = nullptr;
node::FixupMain(argc, raw_argv, &argv);
std::vector<std::string> args(argv, argv + argc);
std::shared_ptr<node::InitializationResult> 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<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]
// [--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<std::string> filtered_args;
bool is_building_snapshot = false;
bool snapshot_as_file = false;
std::optional<node::SnapshotConfig> 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<node::SnapshotFlags>(
static_cast<uint32_t>(snapshot_config.value().flags) |
static_cast<uint32_t>(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<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 (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<std::string> errors;
std::unique_ptr<CommonEnvironmentSetup> 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<Value> 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<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;
}