mirror of
https://github.com/nodejs/node.git
synced 2025-05-13 05:38:36 +00:00

Notable changes: assert: * (SEMVER-MINOR) port common.mustCall() to assert (ConorDavenport) https://github.com/nodejs/node/pull/31982 async_hooks: * (SEMVER-MINOR) add AsyncResource.bind utility (James M Snell) https://github.com/nodejs/node/pull/34574 buffer: * (SEMVER-MINOR) also alias BigUInt methods (Anna Henningsen) https://github.com/nodejs/node/pull/34960 * (SEMVER-MINOR) alias UInt ➡️ Uint in buffer methods (Anna Henningsen) https://github.com/nodejs/node/pull/34729 build: * (SEMVER-MINOR) add build flag for OSS-Fuzz integration (davkor) https://github.com/nodejs/node/pull/34761 cli: * (SEMVER-MINOR) add alias for report-directory to make it consistent (Ash Cripps) https://github.com/nodejs/node/pull/33587 crypto: * (SEMVER-MINOR) allow KeyObjects in postMessage (Tobias Nießen) https://github.com/nodejs/node/pull/33360 * (SEMVER-MINOR) add randomInt function (Oli Lalonde) https://github.com/nodejs/node/pull/34600 deps: * upgrade to libuv 1.39.0 (Colin Ihrig) https://github.com/nodejs/node/pull/34915 * upgrade npm to 6.14.7 (claudiahdz) https://github.com/nodejs/node/pull/34468 * upgrade to libuv 1.38.1 (Colin Ihrig) https://github.com/nodejs/node/pull/34187 dgram: * (SEMVER-MINOR) add IPv6 scope id suffix to received udp6 dgrams (Pekka Nikander) https://github.com/nodejs/node/pull/14500 * (SEMVER-MINOR) allow typed arrays in .send() (Sarat Addepalli) https://github.com/nodejs/node/pull/22413 doc: * (SEMVER-MINOR) Add maxTotalSockets option to agent constructor (rickyes) https://github.com/nodejs/node/pull/33617 * (SEMVER-MINOR) add basic embedding example documentation (Anna Henningsen) https://github.com/nodejs/node/pull/30467 * add Ricky Zhou to collaborators (rickyes) https://github.com/nodejs/node/pull/34676 * add release key for Ruy Adorno (Ruy Adorno) https://github.com/nodejs/node/pull/34628 * add DerekNonGeneric to collaborators (Derek Lewis) https://github.com/nodejs/node/pull/34602 * add AshCripps to collaborators (Ash Cripps) https://github.com/nodejs/node/pull/34494 * add HarshithaKP to collaborators (Harshitha K P) https://github.com/nodejs/node/pull/34417 * add rexagod to collaborators (Pranshu Srivastava) https://github.com/nodejs/node/pull/34457 * add release key for Richard Lau (Richard Lau) https://github.com/nodejs/node/pull/34397 * add danielleadams to collaborators (Danielle Adams) https://github.com/nodejs/node/pull/34360 * add sxa as collaborator (Stewart X Addison) https://github.com/nodejs/node/pull/34338 * add ruyadorno to collaborators (Ruy Adorno) https://github.com/nodejs/node/pull/34297 * (SEMVER-MAJOR) deprecate process.umask() with no arguments (Colin Ihrig) https://github.com/nodejs/node/pull/32499 embedding: * (SEMVER-MINOR) make Stop() stop Workers (Anna Henningsen) https://github.com/nodejs/node/pull/32531 * (SEMVER-MINOR) provide hook for custom process.exit() behaviour (Anna Henningsen) https://github.com/nodejs/node/pull/32531 fs: * (SEMVER-MINOR) implement lutimes (Maël Nison) https://github.com/nodejs/node/pull/33399 http: * (SEMVER-MINOR) add maxTotalSockets to agent class (rickyes) https://github.com/nodejs/node/pull/33617 * (SEMVER-MINOR) return this from IncomingMessage#destroy() (Colin Ihrig) https://github.com/nodejs/node/pull/32789 * (SEMVER-MINOR) expose host and protocol on ClientRequest (wenningplus) https://github.com/nodejs/node/pull/33803 http2: * (SEMVER-MINOR) return this for Http2ServerRequest#setTimeout (Pranshu Srivastava) https://github.com/nodejs/node/pull/33994 * (SEMVER-MINOR) do not modify explicity set date headers (Pranshu Srivastava) https://github.com/nodejs/node/pull/33160 module: * (SEMVER-MINOR) named exports for CJS via static analysis (Guy Bedford) https://github.com/nodejs/node/pull/35249 * (SEMVER-MINOR) exports pattern support (Guy Bedford) https://github.com/nodejs/node/pull/34718 * (SEMVER-MINOR) package "imports" field (Guy Bedford) https://github.com/nodejs/node/pull/34117 * (SEMVER-MINOR) deprecate module.parent (Antoine du HAMEL) https://github.com/nodejs/node/pull/32217 n-api: * (SEMVER-MINOR) create N-API version 7 (Gabriel Schulhof) https://github.com/nodejs/node/pull/35199 * (SEMVER-MINOR) support type-tagging objects (Gabriel Schulhof) https://github.com/nodejs/node/pull/28237 n-api,src: * (SEMVER-MINOR) provide asynchronous cleanup hooks (Anna Henningsen) https://github.com/nodejs/node/pull/34572 perf_hooks: * (SEMVER-MINOR) add idleTime and event loop util (Trevor Norris) https://github.com/nodejs/node/pull/34938 timers: * (SEMVER-MINOR) allow timers to be used as primitives (Denys Otrishko) https://github.com/nodejs/node/pull/34017 tls: * (SEMVER-MINOR) make 'createSecureContext' honor more options (Mateusz Krawczuk) https://github.com/nodejs/node/pull/33974 worker: * (SEMVER-MINOR) add public method for marking objects as untransferable (Anna Henningsen) https://github.com/nodejs/node/pull/33979 * (SEMVER-MINOR) emit `'messagerror'` events for failed deserialization (Anna Henningsen) https://github.com/nodejs/node/pull/33772 * (SEMVER-MINOR) allow passing JS wrapper objects via postMessage (Anna Henningsen) https://github.com/nodejs/node/pull/33772 * (SEMVER-MINOR) allow transferring/cloning generic BaseObjects (Anna Henningsen) https://github.com/nodejs/node/pull/33772 * (SEMVER-MINOR) add option to track unmanaged file descriptors (Anna Henningsen) https://github.com/nodejs/node/pull/34303 * (SEMVER-MINOR) add stack size resource limit option (Anna Henningsen) https://github.com/nodejs/node/pull/33085 worker,fs: * (SEMVER-MINOR) make FileHandle transferable (Anna Henningsen) https://github.com/nodejs/node/pull/33772 zlib: * (SEMVER-MINOR) add `maxOutputLength` option (unknown) https://github.com/nodejs/node/pull/33516 * switch to lazy init for zlib streams (Andrey Pechkurov) https://github.com/nodejs/node/pull/34048 PR-URL: https://github.com/nodejs/node/pull/35401
228 lines
8.6 KiB
Markdown
228 lines
8.6 KiB
Markdown
# C++ embedder API
|
|
|
|
<!--introduced_in=v12.19.0-->
|
|
|
|
Node.js provides a number of C++ APIs that can be used to execute JavaScript
|
|
in a Node.js environment from other C++ software.
|
|
|
|
The documentation for these APIs can be found in [src/node.h][] in the Node.js
|
|
source tree. In addition to the APIs exposed by Node.js, some required concepts
|
|
are provided by the V8 embedder API.
|
|
|
|
Because using Node.js as an embedded library is different from writing code
|
|
that is executed by Node.js, breaking changes do not follow typical Node.js
|
|
[deprecation policy][] and may occur on each semver-major release without prior
|
|
warning.
|
|
|
|
## Example embedding application
|
|
|
|
The following sections will provide an overview over how to use these APIs
|
|
to create an application from scratch that will perform the equivalent of
|
|
`node -e <code>`, i.e. that will take a piece of JavaScript and run it in
|
|
a Node.js-specific environment.
|
|
|
|
The full code can be found [in the Node.js source tree][embedtest.cc].
|
|
|
|
### Setting up per-process state
|
|
|
|
Node.js requires some per-process state management in order to run:
|
|
|
|
* Arguments parsing for Node.js [CLI options][],
|
|
* V8 per-process requirements, such as a `v8::Platform` instance.
|
|
|
|
The following example shows how these can be set up. Some class names are from
|
|
the `node` and `v8` C++ namespaces, respectively.
|
|
|
|
```cpp
|
|
int main(int argc, char** argv) {
|
|
argv = uv_setup_args(argc, argv);
|
|
std::vector<std::string> args(argv, argv + argc);
|
|
std::vector<std::string> exec_args;
|
|
std::vector<std::string> errors;
|
|
// Parse Node.js CLI options, and print any errors that have occurred while
|
|
// trying to parse them.
|
|
int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors);
|
|
for (const std::string& error : errors)
|
|
fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
|
|
if (exit_code != 0) {
|
|
return 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(), args, exec_args);
|
|
|
|
V8::Dispose();
|
|
V8::ShutdownPlatform();
|
|
return ret;
|
|
}
|
|
```
|
|
|
|
### Per-instance state
|
|
|
|
Node.js has a concept of a “Node.js instance”, that is commonly being referred
|
|
to as `node::Environment`. Each `node::Environment` is associated with:
|
|
|
|
* Exactly one `v8::Isolate`, i.e. one JS Engine instance,
|
|
* Exactly one `uv_loop_t`, i.e. one event loop, and
|
|
* A number of `v8::Context`s, but exactly one main `v8::Context`.
|
|
* One `node::IsolateData` instance that contains information that could be
|
|
shared by multiple `node::Environment`s that use the same `v8::Isolate`.
|
|
Currently, no testing if performed for this scenario.
|
|
|
|
In order to set up a `v8::Isolate`, an `v8::ArrayBuffer::Allocator` needs
|
|
to be provided. One possible choice is the default Node.js allocator, which
|
|
can be created through `node::ArrayBufferAllocator::Create()`. Using the Node.js
|
|
allocator allows minor performance optimizations when addons use the Node.js
|
|
C++ `Buffer` API, and is required in order to track `ArrayBuffer` memory in
|
|
[`process.memoryUsage()`][].
|
|
|
|
Additionally, each `v8::Isolate` that is used for a Node.js instance needs to
|
|
be registered and unregistered with the `MultiIsolatePlatform` instance, if one
|
|
is being used, in order for the platform to know which event loop to use
|
|
for tasks scheduled by the `v8::Isolate`.
|
|
|
|
The `node::NewIsolate()` helper function creates a `v8::Isolate`,
|
|
sets it up with some Node.js-specific hooks (e.g. the Node.js error handler),
|
|
and registers it with the platform automatically.
|
|
|
|
```cpp
|
|
int RunNodeInstance(MultiIsolatePlatform* platform,
|
|
const std::vector<std::string>& args,
|
|
const std::vector<std::string>& exec_args) {
|
|
int exit_code = 0;
|
|
// Set up a libuv event loop.
|
|
uv_loop_t loop;
|
|
int ret = uv_loop_init(&loop);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "%s: Failed to initialize loop: %s\n",
|
|
args[0].c_str(),
|
|
uv_err_name(ret));
|
|
return 1;
|
|
}
|
|
|
|
std::shared_ptr<ArrayBufferAllocator> allocator =
|
|
ArrayBufferAllocator::Create();
|
|
|
|
Isolate* isolate = NewIsolate(allocator, &loop, platform);
|
|
if (isolate == nullptr) {
|
|
fprintf(stderr, "%s: Failed to initialize V8 Isolate\n", args[0].c_str());
|
|
return 1;
|
|
}
|
|
|
|
{
|
|
Locker locker(isolate);
|
|
Isolate::Scope isolate_scope(isolate);
|
|
|
|
// Create a node::IsolateData instance that will later be released using
|
|
// node::FreeIsolateData().
|
|
std::unique_ptr<IsolateData, decltype(&node::FreeIsolateData)> isolate_data(
|
|
node::CreateIsolateData(isolate, &loop, platform, allocator.get()),
|
|
node::FreeIsolateData);
|
|
|
|
// Set up a new v8::Context.
|
|
HandleScope handle_scope(isolate);
|
|
Local<Context> context = node::NewContext(isolate);
|
|
if (context.IsEmpty()) {
|
|
fprintf(stderr, "%s: Failed to initialize V8 Context\n", args[0].c_str());
|
|
return 1;
|
|
}
|
|
|
|
// The v8::Context needs to be entered when node::CreateEnvironment() and
|
|
// node::LoadEnvironment() are being called.
|
|
Context::Scope context_scope(context);
|
|
|
|
// Create a node::Environment instance that will later be released using
|
|
// node::FreeEnvironment().
|
|
std::unique_ptr<Environment, decltype(&node::FreeEnvironment)> env(
|
|
node::CreateEnvironment(isolate_data.get(), context, args, exec_args),
|
|
node::FreeEnvironment);
|
|
|
|
// 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.get(),
|
|
"const publicRequire ="
|
|
" require('module').createRequire(process.cwd() + '/');"
|
|
"globalThis.require = publicRequire;"
|
|
"require('vm').runInThisContext(process.argv[1]);");
|
|
|
|
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
|
|
return 1;
|
|
|
|
{
|
|
// SealHandleScope protects against handle leaks from callbacks.
|
|
SealHandleScope seal(isolate);
|
|
bool more;
|
|
do {
|
|
uv_run(&loop, UV_RUN_DEFAULT);
|
|
|
|
// V8 tasks on background threads may end up scheduling new tasks in the
|
|
// foreground, which in turn can keep the event loop going. For example,
|
|
// WebAssembly.compile() may do so.
|
|
platform->DrainTasks(isolate);
|
|
|
|
// If there are new tasks, continue.
|
|
more = uv_loop_alive(&loop);
|
|
if (more) continue;
|
|
|
|
// node::EmitBeforeExit() is used to emit the 'beforeExit' event on
|
|
// the `process` object.
|
|
node::EmitBeforeExit(env.get());
|
|
|
|
// 'beforeExit' can also schedule new work that keeps the event loop
|
|
// running.
|
|
more = uv_loop_alive(&loop);
|
|
} while (more == true);
|
|
}
|
|
|
|
// node::EmitExit() returns the current exit code.
|
|
exit_code = node::EmitExit(env.get());
|
|
|
|
// 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.get());
|
|
}
|
|
|
|
// Unregister the Isolate with the platform and add a listener that is called
|
|
// when the Platform is done cleaning up any state it had associated with
|
|
// the Isolate.
|
|
bool platform_finished = false;
|
|
platform->AddIsolateFinishedCallback(isolate, [](void* data) {
|
|
*static_cast<bool*>(data) = true;
|
|
}, &platform_finished);
|
|
platform->UnregisterIsolate(isolate);
|
|
isolate->Dispose();
|
|
|
|
// Wait until the platform has cleaned up all relevant resources.
|
|
while (!platform_finished)
|
|
uv_run(&loop, UV_RUN_ONCE);
|
|
int err = uv_loop_close(&loop);
|
|
assert(err == 0);
|
|
|
|
return exit_code;
|
|
}
|
|
```
|
|
|
|
[CLI options]: cli.md
|
|
[`process.memoryUsage()`]: process.md#process_process_memoryusage
|
|
[deprecation policy]: deprecations.md
|
|
[embedtest.cc]: https://github.com/nodejs/node/blob/master/test/embedding/embedtest.cc
|
|
[src/node.h]: https://github.com/nodejs/node/blob/master/src/node.h
|