@@ -172,3 +172,219 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
172172[ deprecation policy ] : deprecations.md
173173[ embedtest.cc ] : https://github.com/nodejs/node/blob/HEAD/test/embedding/embedtest.cc
174174[ src/node.h ] : https://github.com/nodejs/node/blob/HEAD/src/node.h
175+ # C++ embedder API
176+
177+ <!-- introduced_in=v12.19.0-->
178+
179+ Node.js provides a number of C++ APIs that can be used to execute JavaScript
180+ in a Node.js environment from other C++ software.
181+
182+ The documentation for these APIs can be found in [ src/node.h] [ ] in the Node.js
183+ source tree. In addition to the APIs exposed by Node.js, some required concepts
184+ are provided by the V8 embedder API.
185+
186+ Because using Node.js as an embedded library is different from writing code
187+ that is executed by Node.js, breaking changes do not follow typical Node.js
188+ [ deprecation policy] [ ] and may occur on each semver-major release without prior
189+ warning.
190+
191+ ## Example embedding application
192+
193+ The following sections will provide an overview over how to use these APIs
194+ to create an application from scratch that will perform the equivalent of
195+ ` node -e <code> ` , i.e. that will take a piece of JavaScript and run it in
196+ a Node.js-specific environment.
197+
198+ The full code can be found [ in the Node.js source tree] [ embedtest.cc ] .
199+
200+ ### Setting up a per-process state
201+
202+ Node.js requires some per-process state management in order to run:
203+
204+ * Arguments parsing for Node.js [ CLI options] [ ] ,
205+ * V8 per-process requirements, such as a ` v8::Platform ` instance.
206+
207+ The following example shows how these can be set up. Some class names are from
208+ the ` node ` and ` v8 ` C++ namespaces, respectively.
209+
210+ ``` cpp
211+ int main (int argc, char** argv) {
212+ argv = uv_setup_args(argc, argv);
213+ std::vector< std::string > args(argv, argv + argc);
214+ // Parse Node.js CLI options, and print any errors that have occurred while
215+ // trying to parse them.
216+ std::unique_ptr< node::InitializationResult > result =
217+ node::InitializeOncePerProcess(args, {
218+ node::ProcessInitializationFlags::kNoInitializeV8,
219+ node::ProcessInitializationFlags::kNoInitializeNodeV8Platform
220+ });
221+
222+ for (const std::string& error : result->errors())
223+ fprintf(stderr, "%s: %s\n", args[ 0] .c_str(), error.c_str());
224+ if (result->early_return() != 0) {
225+ return result->exit_code();
226+ }
227+
228+ // Create a v8::Platform instance. ` MultiIsolatePlatform::Create() ` is a way
229+ // to create a v8::Platform instance that Node.js can use when creating
230+ // Worker threads. When no ` MultiIsolatePlatform ` instance is present,
231+ // Worker threads are disabled.
232+ std::unique_ptr<MultiIsolatePlatform > platform =
233+ MultiIsolatePlatform::Create(4);
234+ V8::InitializePlatform(platform.get());
235+ V8::Initialize();
236+
237+ // See below for the contents of this function.
238+ int ret = RunNodeInstance(
239+ platform.get(), result->args(), result->exec_args());
240+
241+ V8::Dispose();
242+ V8::DisposePlatform();
243+
244+ node::TearDownOncePerProcess();
245+ return ret;
246+ }
247+ ```
248+
249+ ### Setting up a per-instance state
250+
251+ <!-- YAML
252+ changes:
253+ - version: v15.0.0
254+ pr-url: https://github.com/nodejs/node/pull/35597
255+ description:
256+ The `CommonEnvironmentSetup` and `SpinEventLoop` utilities were added.
257+ -->
258+
259+ Node.js has a concept of a “Node.js instance”, that is commonly being referred
260+ to as `node::Environment`. Each `node::Environment` is associated with:
261+
262+ * Exactly one `v8::Isolate`, i.e. one JS Engine instance,
263+ * Exactly one `uv_loop_t`, i.e. one event loop,
264+ * A number of `v8::Context`s, but exactly one main `v8::Context`, and
265+ * One `node::IsolateData` instance that contains information that could be
266+ shared by multiple `node::Environment`s. The embedder should make sure
267+ that `node::IsolateData` is shared only among `node::Environment`s that
268+ use the same `v8::Isolate`, Node.js does not perform this check.
269+
270+ In order to set up a `v8::Isolate`, an `v8::ArrayBuffer::Allocator` needs
271+ to be provided. One possible choice is the default Node.js allocator, which
272+ can be created through `node::ArrayBufferAllocator::Create()`. Using the Node.js
273+ allocator allows minor performance optimizations when addons use the Node.js
274+ C++ `Buffer` API, and is required in order to track `ArrayBuffer` memory in
275+ [`process.memoryUsage()`][].
276+
277+ Additionally, each `v8::Isolate` that is used for a Node.js instance needs to
278+ be registered and unregistered with the `MultiIsolatePlatform` instance, if one
279+ is being used, in order for the platform to know which event loop to use
280+ for tasks scheduled by the `v8::Isolate`.
281+
282+ The `node::NewIsolate()` helper function creates a `v8::Isolate`,
283+ sets it up with some Node.js-specific hooks (e.g. the Node.js error handler),
284+ and registers it with the platform automatically.
285+
286+ ```cpp
287+ int RunNodeInstance(MultiIsolatePlatform* platform,
288+ const std::vector<std::string>& args,
289+ const std::vector<std::string>& exec_args) {
290+ int exit_code = 0;
291+
292+ // Setup up a libuv event loop, v8::Isolate, and Node.js Environment.
293+ std::vector<std::string> errors;
294+ std::unique_ptr<CommonEnvironmentSetup> setup =
295+ CommonEnvironmentSetup::Create(platform, &errors, args, exec_args);
296+ if (!setup) {
297+ for (const std::string& err : errors)
298+ fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
299+ return 1;
300+ }
301+
302+ Isolate* isolate = setup->isolate();
303+ Environment* env = setup->env();
304+
305+ {
306+ Locker locker(isolate);
307+ Isolate::Scope isolate_scope(isolate);
308+ HandleScope handle_scope(isolate);
309+ // The v8::Context needs to be entered when node::CreateEnvironment() and
310+ // node::LoadEnvironment() are being called.
311+ Context::Scope context_scope(setup->context());
312+
313+ // Set up the Node.js instance for execution, and run code inside of it.
314+ // There is also a variant that takes a callback and provides it with
315+ // the `require` and `process` objects, so that it can manually compile
316+ // and run scripts as needed.
317+ // The `require` function inside this script does *not* access the file
318+ // system, and can only load built-in Node.js modules.
319+ // `module.createRequire()` is being used to create one that is able to
320+ // load files from the disk, and uses the standard CommonJS file loader
321+ // instead of the internal-only `require` function.
322+ MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
323+ env,
324+ "const publicRequire ="
325+ " require('node:module').createRequire(process.cwd() + '/');"
326+ "globalThis.require = publicRequire;"
327+ "require('node:vm').runInThisContext(process.argv[1]);");
328+
329+ if (loadenv_ret.IsEmpty()) // There has been a JS exception.
330+ return 1;
331+
332+ exit_code = node::SpinEventLoop(env).FromMaybe(1);
333+
334+ // node::Stop() can be used to explicitly stop the event loop and keep
335+ // further JavaScript from running. It can be called from any thread,
336+ // and will act like worker.terminate() if called from another thread.
337+ node::Stop(env);
338+ }
339+
340+ return exit_code;
341+ }
342+ ```
343+
344+ ## C runtime API
345+
346+ <!-- introduced_in=REPLACEME-->
347+
348+ While Node.js provides an extensive C++ embedding API that can be used from C++
349+ applications, the C-based API is useful when Node.js is embedded as a shared
350+ libnode library into C++ or non-C++ applications.
351+
352+ ### API design overview
353+
354+ One of the goals for the C based runtime API is to be ABI stable. It means that
355+ applications must be able to use newer libnode versions without recompilation.
356+ The following design principles are targeting to achieve that goal.
357+
358+ * Follow the best practices for the [ node-api] [ ] design and build on top of
359+ the [ node-api] [ ] .
360+
361+ ### API reference
362+
363+ #### Functions
364+
365+ ##### ` node_embed_start `
366+
367+ <!-- YAML
368+ added: REPLACEME
369+ -->
370+
371+ > Stability: 1 - Experimental
372+
373+ Runs Node.js runtime instance the same way as the Node.js executable.
374+
375+ ``` c
376+ int32_t NAPI_CDECL node_embed_start (
377+ int32_t argc,
378+ char* argv[ ] );
379+ ```
380+
381+ * `[in] argc`: Number of items in the `argv` array.
382+ * `[in] argv`: CLI arguments as an array of zero terminated strings.
383+ Returns `int32_t` with runtime instance exit code.
384+
385+ [CLI options]: cli.md
386+ [`process.memoryUsage()`]: process.md#processmemoryusage
387+ [deprecation policy]: deprecations.md
388+ [embedtest.cc]: https://github.com/nodejs/node/blob/HEAD/test/embedding/embedtest.cc
389+ [node-api]: n-api.md
390+ [src/node.h]: https://github.com/nodejs/node/blob/HEAD/src/node.h
0 commit comments