node/test/embedding/embedtest.cc
Joyee Cheung 9aa1afb527
src: use V8-owned CppHeap
As V8 is moving towards built-in CppHeap creation, change the
management so that the automatic CppHeap creation on Node.js's end
is also enforced at Isolate creation time.

1. If embedder uses NewIsolate(), either they use
  IsolateSettings::cpp_heap to specify a CppHeap that will be owned
  by V8, or if it's not configured, Node.js will create a CppHeap
  that will be owned by V8.
2. If the embedder uses SetIsolateUpForNode(),
  IsolateSettings::cpp_heap will be ignored (as V8 has deprecated
  attaching CppHeap post-isolate-creation). The embedders need to
  ensure that the v8::Isolate has a CppHeap attached while it's
  still used by Node.js, preferably using v8::CreateParams.

See https://issues.chromium.org/issues/42203693 for details. In
future version of V8, this CppHeap will be created by V8 if not
provided, and we can remove our own "if no CppHeap provided,
create one" code in NewIsolate().

PR-URL: https://github.com/nodejs/node/pull/58070
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
2025-05-02 15:10:38 +02:00

235 lines
7.9 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 "cppgc/platform.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,
node::ProcessInitializationFlags::kNoInitializeCppgc,
});
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());
cppgc::InitializeProcess(platform->GetPageAllocator());
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;
}