11cb0ef41Sopenharmony_ci# C++ embedder API
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ci<!--introduced_in=v12.19.0-->
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciNode.js provides a number of C++ APIs that can be used to execute JavaScript
61cb0ef41Sopenharmony_ciin a Node.js environment from other C++ software.
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciThe documentation for these APIs can be found in [src/node.h][] in the Node.js
91cb0ef41Sopenharmony_cisource tree. In addition to the APIs exposed by Node.js, some required concepts
101cb0ef41Sopenharmony_ciare provided by the V8 embedder API.
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ciBecause using Node.js as an embedded library is different from writing code
131cb0ef41Sopenharmony_cithat is executed by Node.js, breaking changes do not follow typical Node.js
141cb0ef41Sopenharmony_ci[deprecation policy][] and may occur on each semver-major release without prior
151cb0ef41Sopenharmony_ciwarning.
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci## Example embedding application
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ciThe following sections will provide an overview over how to use these APIs
201cb0ef41Sopenharmony_cito create an application from scratch that will perform the equivalent of
211cb0ef41Sopenharmony_ci`node -e <code>`, i.e. that will take a piece of JavaScript and run it in
221cb0ef41Sopenharmony_cia Node.js-specific environment.
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ciThe full code can be found [in the Node.js source tree][embedtest.cc].
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci### Setting up per-process state
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ciNode.js requires some per-process state management in order to run:
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_ci* Arguments parsing for Node.js [CLI options][],
311cb0ef41Sopenharmony_ci* V8 per-process requirements, such as a `v8::Platform` instance.
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ciThe following example shows how these can be set up. Some class names are from
341cb0ef41Sopenharmony_cithe `node` and `v8` C++ namespaces, respectively.
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ci```cpp
371cb0ef41Sopenharmony_ciint main(int argc, char** argv) {
381cb0ef41Sopenharmony_ci  argv = uv_setup_args(argc, argv);
391cb0ef41Sopenharmony_ci  std::vector<std::string> args(argv, argv + argc);
401cb0ef41Sopenharmony_ci  // Parse Node.js CLI options, and print any errors that have occurred while
411cb0ef41Sopenharmony_ci  // trying to parse them.
421cb0ef41Sopenharmony_ci  std::unique_ptr<node::InitializationResult> result =
431cb0ef41Sopenharmony_ci      node::InitializeOncePerProcess(args, {
441cb0ef41Sopenharmony_ci        node::ProcessInitializationFlags::kNoInitializeV8,
451cb0ef41Sopenharmony_ci        node::ProcessInitializationFlags::kNoInitializeNodeV8Platform
461cb0ef41Sopenharmony_ci      });
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci  for (const std::string& error : result->errors())
491cb0ef41Sopenharmony_ci    fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
501cb0ef41Sopenharmony_ci  if (result->early_return() != 0) {
511cb0ef41Sopenharmony_ci    return result->exit_code();
521cb0ef41Sopenharmony_ci  }
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci  // Create a v8::Platform instance. `MultiIsolatePlatform::Create()` is a way
551cb0ef41Sopenharmony_ci  // to create a v8::Platform instance that Node.js can use when creating
561cb0ef41Sopenharmony_ci  // Worker threads. When no `MultiIsolatePlatform` instance is present,
571cb0ef41Sopenharmony_ci  // Worker threads are disabled.
581cb0ef41Sopenharmony_ci  std::unique_ptr<MultiIsolatePlatform> platform =
591cb0ef41Sopenharmony_ci      MultiIsolatePlatform::Create(4);
601cb0ef41Sopenharmony_ci  V8::InitializePlatform(platform.get());
611cb0ef41Sopenharmony_ci  V8::Initialize();
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci  // See below for the contents of this function.
641cb0ef41Sopenharmony_ci  int ret = RunNodeInstance(
651cb0ef41Sopenharmony_ci      platform.get(), result->args(), result->exec_args());
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci  V8::Dispose();
681cb0ef41Sopenharmony_ci  V8::DisposePlatform();
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci  node::TearDownOncePerProcess();
711cb0ef41Sopenharmony_ci  return ret;
721cb0ef41Sopenharmony_ci}
731cb0ef41Sopenharmony_ci```
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci### Per-instance state
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci<!-- YAML
781cb0ef41Sopenharmony_cichanges:
791cb0ef41Sopenharmony_ci  - version: v15.0.0
801cb0ef41Sopenharmony_ci    pr-url: https://github.com/nodejs/node/pull/35597
811cb0ef41Sopenharmony_ci    description:
821cb0ef41Sopenharmony_ci      The `CommonEnvironmentSetup` and `SpinEventLoop` utilities were added.
831cb0ef41Sopenharmony_ci-->
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ciNode.js has a concept of a “Node.js instance”, that is commonly being referred
861cb0ef41Sopenharmony_cito as `node::Environment`. Each `node::Environment` is associated with:
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci* Exactly one `v8::Isolate`, i.e. one JS Engine instance,
891cb0ef41Sopenharmony_ci* Exactly one `uv_loop_t`, i.e. one event loop, and
901cb0ef41Sopenharmony_ci* A number of `v8::Context`s, but exactly one main `v8::Context`.
911cb0ef41Sopenharmony_ci* One `node::IsolateData` instance that contains information that could be
921cb0ef41Sopenharmony_ci  shared by multiple `node::Environment`s that use the same `v8::Isolate`.
931cb0ef41Sopenharmony_ci  Currently, no testing if performed for this scenario.
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ciIn order to set up a `v8::Isolate`, an `v8::ArrayBuffer::Allocator` needs
961cb0ef41Sopenharmony_cito be provided. One possible choice is the default Node.js allocator, which
971cb0ef41Sopenharmony_cican be created through `node::ArrayBufferAllocator::Create()`. Using the Node.js
981cb0ef41Sopenharmony_ciallocator allows minor performance optimizations when addons use the Node.js
991cb0ef41Sopenharmony_ciC++ `Buffer` API, and is required in order to track `ArrayBuffer` memory in
1001cb0ef41Sopenharmony_ci[`process.memoryUsage()`][].
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ciAdditionally, each `v8::Isolate` that is used for a Node.js instance needs to
1031cb0ef41Sopenharmony_cibe registered and unregistered with the `MultiIsolatePlatform` instance, if one
1041cb0ef41Sopenharmony_ciis being used, in order for the platform to know which event loop to use
1051cb0ef41Sopenharmony_cifor tasks scheduled by the `v8::Isolate`.
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ciThe `node::NewIsolate()` helper function creates a `v8::Isolate`,
1081cb0ef41Sopenharmony_cisets it up with some Node.js-specific hooks (e.g. the Node.js error handler),
1091cb0ef41Sopenharmony_ciand registers it with the platform automatically.
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci```cpp
1121cb0ef41Sopenharmony_ciint RunNodeInstance(MultiIsolatePlatform* platform,
1131cb0ef41Sopenharmony_ci                    const std::vector<std::string>& args,
1141cb0ef41Sopenharmony_ci                    const std::vector<std::string>& exec_args) {
1151cb0ef41Sopenharmony_ci  int exit_code = 0;
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci  // Setup up a libuv event loop, v8::Isolate, and Node.js Environment.
1181cb0ef41Sopenharmony_ci  std::vector<std::string> errors;
1191cb0ef41Sopenharmony_ci  std::unique_ptr<CommonEnvironmentSetup> setup =
1201cb0ef41Sopenharmony_ci      CommonEnvironmentSetup::Create(platform, &errors, args, exec_args);
1211cb0ef41Sopenharmony_ci  if (!setup) {
1221cb0ef41Sopenharmony_ci    for (const std::string& err : errors)
1231cb0ef41Sopenharmony_ci      fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
1241cb0ef41Sopenharmony_ci    return 1;
1251cb0ef41Sopenharmony_ci  }
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_ci  Isolate* isolate = setup->isolate();
1281cb0ef41Sopenharmony_ci  Environment* env = setup->env();
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci  {
1311cb0ef41Sopenharmony_ci    Locker locker(isolate);
1321cb0ef41Sopenharmony_ci    Isolate::Scope isolate_scope(isolate);
1331cb0ef41Sopenharmony_ci    HandleScope handle_scope(isolate);
1341cb0ef41Sopenharmony_ci    // The v8::Context needs to be entered when node::CreateEnvironment() and
1351cb0ef41Sopenharmony_ci    // node::LoadEnvironment() are being called.
1361cb0ef41Sopenharmony_ci    Context::Scope context_scope(setup->context());
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ci    // Set up the Node.js instance for execution, and run code inside of it.
1391cb0ef41Sopenharmony_ci    // There is also a variant that takes a callback and provides it with
1401cb0ef41Sopenharmony_ci    // the `require` and `process` objects, so that it can manually compile
1411cb0ef41Sopenharmony_ci    // and run scripts as needed.
1421cb0ef41Sopenharmony_ci    // The `require` function inside this script does *not* access the file
1431cb0ef41Sopenharmony_ci    // system, and can only load built-in Node.js modules.
1441cb0ef41Sopenharmony_ci    // `module.createRequire()` is being used to create one that is able to
1451cb0ef41Sopenharmony_ci    // load files from the disk, and uses the standard CommonJS file loader
1461cb0ef41Sopenharmony_ci    // instead of the internal-only `require` function.
1471cb0ef41Sopenharmony_ci    MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
1481cb0ef41Sopenharmony_ci        env,
1491cb0ef41Sopenharmony_ci        "const publicRequire ="
1501cb0ef41Sopenharmony_ci        "  require('node:module').createRequire(process.cwd() + '/');"
1511cb0ef41Sopenharmony_ci        "globalThis.require = publicRequire;"
1521cb0ef41Sopenharmony_ci        "require('node:vm').runInThisContext(process.argv[1]);");
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_ci    if (loadenv_ret.IsEmpty())  // There has been a JS exception.
1551cb0ef41Sopenharmony_ci      return 1;
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ci    exit_code = node::SpinEventLoop(env).FromMaybe(1);
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_ci    // node::Stop() can be used to explicitly stop the event loop and keep
1601cb0ef41Sopenharmony_ci    // further JavaScript from running. It can be called from any thread,
1611cb0ef41Sopenharmony_ci    // and will act like worker.terminate() if called from another thread.
1621cb0ef41Sopenharmony_ci    node::Stop(env);
1631cb0ef41Sopenharmony_ci  }
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ci  return exit_code;
1661cb0ef41Sopenharmony_ci}
1671cb0ef41Sopenharmony_ci```
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci[CLI options]: cli.md
1701cb0ef41Sopenharmony_ci[`process.memoryUsage()`]: process.md#processmemoryusage
1711cb0ef41Sopenharmony_ci[deprecation policy]: deprecations.md
1721cb0ef41Sopenharmony_ci[embedtest.cc]: https://github.com/nodejs/node/blob/HEAD/test/embedding/embedtest.cc
1731cb0ef41Sopenharmony_ci[src/node.h]: https://github.com/nodejs/node/blob/HEAD/src/node.h
174