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 <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<std::string> args(argv, argv + argc);\n std::vector<std::string> exec_args;\n std::vector<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(&args, &exec_args, &errors);\n for (const std::string& 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<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<std::string>& args,\n const std::vector<std::string>& 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(&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<ArrayBufferAllocator> allocator =\n ArrayBufferAllocator::Create();\n\n Isolate* isolate = NewIsolate(allocator, &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<IsolateData, decltype(&node::FreeIsolateData)> isolate_data(\n node::CreateIsolateData(isolate, &loop, platform, allocator.get()),\n node::FreeIsolateData);\n\n // Set up a new v8::Context.\n HandleScope handle_scope(isolate);\n Local<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<Environment, decltype(&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<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(&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(&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(&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<bool*>(data) = true;\n }, &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(&loop, UV_RUN_ONCE);\n int err = uv_loop_close(&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}