API для встраивания C++
Node.js предоставляет ряд API C++, которые можно использовать для выполнения JavaScript в среде Node.js из другого программного обеспечения на C++.
Документацию по этим API можно найти в файле src/node.h в дереве исходного кода Node.js. Помимо API, предоставляемых Node.js, некоторые необходимые концепции представлены в API встраивания V8.
Поскольку использование Node.js в качестве встраиваемой библиотеки отличается от написания кода, выполняемого Node.js, критические изменения не следуют стандартной политике устаревания Node.js и могут происходить в каждом выпуске с изменением основной версии semver без предварительного предупреждения.
Пример встраиваемого приложения
В следующих разделах будет представлен обзор использования этих API для создания приложения с нуля, которое выполняет эквивалент node -e <code>, то есть принимает фрагмент JavaScript и запускает его в специфичной для Node.js среде.
Полный код можно найти в дереве исходного кода Node.js.
Настройка состояния для каждого процесса
Для работы Node.js требуется управление состоянием для каждого процесса:
- Разбор аргументов для параметров командной строки Node.js,
- Требования V8 для каждого процесса, например экземпляр
v8::Platform.
В следующем примере показано, как это можно настроить. Некоторые имена классов взяты соответственно из пространств имён C++ node и v8.
int main(int argc, char** argv) {
argv = uv_setup_args(argc, argv);
std::vector<std::string> args(argv, argv + argc);
// Parse Node.js CLI options, and print any errors that have occurred while
// trying to parse them.
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();
}
// Create a v8::Platform instance. `MultiIsolatePlatform::Create()` is a way
// to create a v8::Platform instance that Node.js can use when creating
// Worker threads. When no `MultiIsolatePlatform` instance is present,
// Worker threads are disabled.
std::unique_ptr<MultiIsolatePlatform> platform =
MultiIsolatePlatform::Create(4);
V8::InitializePlatform(platform.get());
V8::Initialize();
// See below for the contents of this function.
int ret = RunNodeInstance(
platform.get(), result->args(), result->exec_args());
V8::Dispose();
V8::DisposePlatform();
node::TearDownOncePerProcess();
return ret;
} copy Настройка состояния для каждого экземпляра
В Node.js используется понятие «экземпляр Node.js», который обычно называют node::Environment. Каждый node::Environment связан со следующими элементами:
- Ровно одним
v8::Isolate, то есть одним экземпляром движка JS, - Ровно одним
uv_loop_t, то есть одним циклом событий, - Несколькими
v8::Context, но ровно одним основнымv8::Context, и - Одним экземпляром
node::IsolateData, содержащим информацию, которой могут совместно пользоваться несколькоnode::Environment. Встраивающая сторона должна обеспечить совместное использованиеnode::IsolateDataтолько междуnode::Environment, использующими один и тот жеv8::Isolate; Node.js не выполняет эту проверку.
Для настройки v8::Isolate необходимо предоставить v8::ArrayBuffer::Allocator. Один из возможных вариантов — стандартный аллокатор Node.js, который можно создать с помощью node::ArrayBufferAllocator::Create(). Использование аллокатора Node.js позволяет выполнять небольшие оптимизации производительности, когда дополнения используют API C++ Buffer Node.js, и необходимо для отслеживания памяти ArrayBuffer в process.memoryUsage().
Кроме того, каждый v8::Isolate, используемый для экземпляра Node.js, необходимо регистрировать и отменять его регистрацию в экземпляре MultiIsolatePlatform, если он используется, чтобы платформа знала, какой цикл событий использовать для задач, запланированных v8::Isolate.
Вспомогательная функция node::NewIsolate() создаёт v8::Isolate, настраивает его с помощью некоторых специфичных для Node.js обработчиков (например, обработчика ошибок Node.js) и автоматически регистрирует его на платформе.
int RunNodeInstance(MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args) {
int exit_code = 0;
// Setup up a libuv event loop, v8::Isolate, and Node.js Environment.
std::vector<std::string> errors;
std::unique_ptr<CommonEnvironmentSetup> setup =
CommonEnvironmentSetup::Create(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 1;
}
Isolate* isolate = setup->isolate();
Environment* env = setup->env();
{
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
// The v8::Context needs to be entered when node::CreateEnvironment() and
// node::LoadEnvironment() are being called.
Context::Scope context_scope(setup->context());
// Set up the Node.js instance for execution, and run code inside of it.
// There is also a variant that takes a callback and provides it with
// the `require` and `process` objects, so that it can manually compile
// and run scripts as needed.
// The `require` function inside this script does *not* access the file
// system, and can only load built-in Node.js modules.
// `module.createRequire()` is being used to create one that is able to
// load files from the disk, and uses the standard CommonJS file loader
// instead of the internal-only `require` function.
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
env,
"const publicRequire ="
" require('node:module').createRequire(process.cwd() + '/');"
"globalThis.require = publicRequire;"
"require('node:vm').runInThisContext(process.argv[1]);");
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
return 1;
exit_code = node::SpinEventLoop(env).FromMaybe(1);
// node::Stop() can be used to explicitly stop the event loop and keep
// further JavaScript from running. It can be called from any thread,
// and will act like worker.terminate() if called from another thread.
node::Stop(env);
}
return exit_code;
} copy
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v20.x/docs/api/embedding.html