• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1{
2  "type": "module",
3  "source": "doc/api/embedding.md",
4  "modules": [
5    {
6      "textRaw": "C++ embedder API",
7      "name": "c++_embedder_api",
8      "introduced_in": "v14.0.0",
9      "desc": "<p>Node.js provides a number of C++ APIs that can be used to execute JavaScript\nin a Node.js environment from other C++ software.</p>\n<p>The documentation for these APIs can be found in <a href=\"https://github.com/nodejs/node/blob/HEAD/src/node.h\">src/node.h</a> in the Node.js\nsource tree. In addition to the APIs exposed by Node.js, some required concepts\nare provided by the V8 embedder API.</p>\n<p>Because using Node.js as an embedded library is different from writing code\nthat is executed by Node.js, breaking changes do not follow typical Node.js\n<a href=\"deprecations.html\">deprecation policy</a> and may occur on each semver-major release without prior\nwarning.</p>\n<h2>Example embedding application</h2>\n<p>The following sections will provide an overview over how to use these APIs\nto create an application from scratch that will perform the equivalent of\n<code>node -e &#x3C;code></code>, i.e. that will take a piece of JavaScript and run it in\na Node.js-specific environment.</p>\n<p>The full code can be found <a href=\"https://github.com/nodejs/node/blob/HEAD/test/embedding/embedtest.cc\">in the Node.js source tree</a>.</p>",
10      "modules": [
11        {
12          "textRaw": "Setting up per-process state",
13          "name": "setting_up_per-process_state",
14          "desc": "<p>Node.js requires some per-process state management in order to run:</p>\n<ul>\n<li>Arguments parsing for Node.js <a href=\"cli.html\">CLI options</a>,</li>\n<li>V8 per-process requirements, such as a <code>v8::Platform</code> instance.</li>\n</ul>\n<p>The following example shows how these can be set up. Some class names are from\nthe <code>node</code> and <code>v8</code> C++ namespaces, respectively.</p>\n<pre><code class=\"language-cpp\">int main(int argc, char** argv) {\n  argv = uv_setup_args(argc, argv);\n  std::vector&#x3C;std::string> args(argv, argv + argc);\n  std::vector&#x3C;std::string> exec_args;\n  std::vector&#x3C;std::string> errors;\n  // Parse Node.js CLI options, and print any errors that have occurred while\n  // trying to parse them.\n  int exit_code = node::InitializeNodeWithArgs(&#x26;args, &#x26;exec_args, &#x26;errors);\n  for (const std::string&#x26; error : errors)\n    fprintf(stderr, \"%s: %s\\n\", args[0].c_str(), error.c_str());\n  if (exit_code != 0) {\n    return exit_code;\n  }\n\n  // Create a v8::Platform instance. `MultiIsolatePlatform::Create()` is a way\n  // to create a v8::Platform instance that Node.js can use when creating\n  // Worker threads. When no `MultiIsolatePlatform` instance is present,\n  // Worker threads are disabled.\n  std::unique_ptr&#x3C;MultiIsolatePlatform> platform =\n      MultiIsolatePlatform::Create(4);\n  V8::InitializePlatform(platform.get());\n  V8::Initialize();\n\n  // See below for the contents of this function.\n  int ret = RunNodeInstance(platform.get(), args, exec_args);\n\n  V8::Dispose();\n  V8::ShutdownPlatform();\n  return ret;\n}\n</code></pre>",
15          "type": "module",
16          "displayName": "Setting up per-process state"
17        },
18        {
19          "textRaw": "Per-instance state",
20          "name": "per-instance_state",
21          "desc": "<p>Node.js has a concept of a “Node.js instance”, that is commonly being referred\nto as <code>node::Environment</code>. Each <code>node::Environment</code> is associated with:</p>\n<ul>\n<li>Exactly one <code>v8::Isolate</code>, i.e. one JS Engine instance,</li>\n<li>Exactly one <code>uv_loop_t</code>, i.e. one event loop, and</li>\n<li>A number of <code>v8::Context</code>s, but exactly one main <code>v8::Context</code>.</li>\n<li>One <code>node::IsolateData</code> instance that contains information that could be\nshared by multiple <code>node::Environment</code>s that use the same <code>v8::Isolate</code>.\nCurrently, no testing if performed for this scenario.</li>\n</ul>\n<p>In order to set up a <code>v8::Isolate</code>, an <code>v8::ArrayBuffer::Allocator</code> needs\nto be provided. One possible choice is the default Node.js allocator, which\ncan be created through <code>node::ArrayBufferAllocator::Create()</code>. Using the Node.js\nallocator allows minor performance optimizations when addons use the Node.js\nC++ <code>Buffer</code> API, and is required in order to track <code>ArrayBuffer</code> memory in\n<a href=\"process.html#process_process_memoryusage\"><code>process.memoryUsage()</code></a>.</p>\n<p>Additionally, each <code>v8::Isolate</code> that is used for a Node.js instance needs to\nbe registered and unregistered with the <code>MultiIsolatePlatform</code> instance, if one\nis being used, in order for the platform to know which event loop to use\nfor tasks scheduled by the <code>v8::Isolate</code>.</p>\n<p>The <code>node::NewIsolate()</code> helper function creates a <code>v8::Isolate</code>,\nsets it up with some Node.js-specific hooks (e.g. the Node.js error handler),\nand registers it with the platform automatically.</p>\n<pre><code class=\"language-cpp\">int RunNodeInstance(MultiIsolatePlatform* platform,\n                    const std::vector&#x3C;std::string>&#x26; args,\n                    const std::vector&#x3C;std::string>&#x26; exec_args) {\n  int exit_code = 0;\n  // Set up a libuv event loop.\n  uv_loop_t loop;\n  int ret = uv_loop_init(&#x26;loop);\n  if (ret != 0) {\n    fprintf(stderr, \"%s: Failed to initialize loop: %s\\n\",\n            args[0].c_str(),\n            uv_err_name(ret));\n    return 1;\n  }\n\n  std::shared_ptr&#x3C;ArrayBufferAllocator> allocator =\n      ArrayBufferAllocator::Create();\n\n  Isolate* isolate = NewIsolate(allocator, &#x26;loop, platform);\n  if (isolate == nullptr) {\n    fprintf(stderr, \"%s: Failed to initialize V8 Isolate\\n\", args[0].c_str());\n    return 1;\n  }\n\n  {\n    Locker locker(isolate);\n    Isolate::Scope isolate_scope(isolate);\n\n    // Create a node::IsolateData instance that will later be released using\n    // node::FreeIsolateData().\n    std::unique_ptr&#x3C;IsolateData, decltype(&#x26;node::FreeIsolateData)> isolate_data(\n        node::CreateIsolateData(isolate, &#x26;loop, platform, allocator.get()),\n        node::FreeIsolateData);\n\n    // Set up a new v8::Context.\n    HandleScope handle_scope(isolate);\n    Local&#x3C;Context> context = node::NewContext(isolate);\n    if (context.IsEmpty()) {\n      fprintf(stderr, \"%s: Failed to initialize V8 Context\\n\", args[0].c_str());\n      return 1;\n    }\n\n    // The v8::Context needs to be entered when node::CreateEnvironment() and\n    // node::LoadEnvironment() are being called.\n    Context::Scope context_scope(context);\n\n    // Create a node::Environment instance that will later be released using\n    // node::FreeEnvironment().\n    std::unique_ptr&#x3C;Environment, decltype(&#x26;node::FreeEnvironment)> env(\n        node::CreateEnvironment(isolate_data.get(), context, args, exec_args),\n        node::FreeEnvironment);\n\n    // Set up the Node.js instance for execution, and run code inside of it.\n    // There is also a variant that takes a callback and provides it with\n    // the `require` and `process` objects, so that it can manually compile\n    // and run scripts as needed.\n    // The `require` function inside this script does *not* access the file\n    // system, and can only load built-in Node.js modules.\n    // `module.createRequire()` is being used to create one that is able to\n    // load files from the disk, and uses the standard CommonJS file loader\n    // instead of the internal-only `require` function.\n    MaybeLocal&#x3C;Value> loadenv_ret = node::LoadEnvironment(\n        env.get(),\n        \"const publicRequire =\"\n        \"  require('module').createRequire(process.cwd() + '/');\"\n        \"globalThis.require = publicRequire;\"\n        \"require('vm').runInThisContext(process.argv[1]);\");\n\n    if (loadenv_ret.IsEmpty())  // There has been a JS exception.\n      return 1;\n\n    {\n      // SealHandleScope protects against handle leaks from callbacks.\n      SealHandleScope seal(isolate);\n      bool more;\n      do {\n        uv_run(&#x26;loop, UV_RUN_DEFAULT);\n\n        // V8 tasks on background threads may end up scheduling new tasks in the\n        // foreground, which in turn can keep the event loop going. For example,\n        // WebAssembly.compile() may do so.\n        platform->DrainTasks(isolate);\n\n        // If there are new tasks, continue.\n        more = uv_loop_alive(&#x26;loop);\n        if (more) continue;\n\n        // node::EmitProcessBeforeExit() is used to emit the 'beforeExit' event\n        // on the `process` object.\n        if (node::EmitProcessBeforeExit(env.get()).IsNothing())\n          break;\n\n        // 'beforeExit' can also schedule new work that keeps the event loop\n        // running.\n        more = uv_loop_alive(&#x26;loop);\n      } while (more == true);\n    }\n\n    // node::EmitProcessExit() returns the current exit code.\n    exit_code = node::EmitProcessExit(env.get()).FromMaybe(1);\n\n    // node::Stop() can be used to explicitly stop the event loop and keep\n    // further JavaScript from running. It can be called from any thread,\n    // and will act like worker.terminate() if called from another thread.\n    node::Stop(env.get());\n  }\n\n  // Unregister the Isolate with the platform and add a listener that is called\n  // when the Platform is done cleaning up any state it had associated with\n  // the Isolate.\n  bool platform_finished = false;\n  platform->AddIsolateFinishedCallback(isolate, [](void* data) {\n    *static_cast&#x3C;bool*>(data) = true;\n  }, &#x26;platform_finished);\n  platform->UnregisterIsolate(isolate);\n  isolate->Dispose();\n\n  // Wait until the platform has cleaned up all relevant resources.\n  while (!platform_finished)\n    uv_run(&#x26;loop, UV_RUN_ONCE);\n  int err = uv_loop_close(&#x26;loop);\n  assert(err == 0);\n\n  return exit_code;\n}\n</code></pre>",
22          "type": "module",
23          "displayName": "Per-instance state"
24        }
25      ],
26      "type": "module",
27      "displayName": "C++ embedder API"
28    }
29  ]
30}