Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1a70820

Browse filesBrowse files
committed
doc: add basic embedding example documentation
Backport-PR-URL: #35241 PR-URL: #30467 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
1 parent 4af336d commit 1a70820
Copy full SHA for 1a70820

File tree

Expand file treeCollapse file tree

2 files changed

+230
-3
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

2 files changed

+230
-3
lines changed
Open diff view settings
Collapse file

‎doc/api/embedding.md‎

Copy file name to clipboard
+226Lines changed: 226 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
# C++ Embedder API
2+
3+
<!--introduced_in=REPLACEME-->
4+
5+
Node.js provides a number of C++ APIs that can be used to execute JavaScript
6+
in a Node.js environment from other C++ software.
7+
8+
The documentation for these APIs can be found in [src/node.h][] in the Node.js
9+
source tree. In addition to the APIs exposed by Node.js, some required concepts
10+
are provided by the V8 embedder API.
11+
12+
Because using Node.js as an embedded library is different from writing code
13+
that is executed by Node.js, breaking changes do not follow typical Node.js
14+
[deprecation policy][] and may occur on each semver-major release without prior
15+
warning.
16+
17+
## Example embedding application
18+
19+
The following sections will provide an overview over how to use these APIs
20+
to create an application from scratch that will perform the equivalent of
21+
`node -e <code>`, i.e. that will take a piece of JavaScript and run it in
22+
a Node.js-specific environment.
23+
24+
The full code can be found [in the Node.js source tree][embedtest.cc].
25+
26+
### Setting up per-process state
27+
28+
Node.js requires some per-process state management in order to run:
29+
30+
* Arguments parsing for Node.js [CLI options][],
31+
* V8 per-process requirements, such as a `v8::Platform` instance.
32+
33+
The following example shows how these can be set up. Some class names are from
34+
the `node` and `v8` C++ namespaces, respectively.
35+
36+
```cpp
37+
int main(int argc, char** argv) {
38+
std::vector<std::string> args(argv, argv + argc);
39+
std::vector<std::string> exec_args;
40+
std::vector<std::string> errors;
41+
// Parse Node.js CLI options, and print any errors that have occurred while
42+
// trying to parse them.
43+
int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors);
44+
for (const std::string& error : errors)
45+
fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
46+
if (exit_code != 0) {
47+
return exit_code;
48+
}
49+
50+
// Create a v8::Platform instance. `MultiIsolatePlatform::Create()` is a way
51+
// to create a v8::Platform instance that Node.js can use when creating
52+
// Worker threads. When no `MultiIsolatePlatform` instance is present,
53+
// Worker threads are disabled.
54+
std::unique_ptr<MultiIsolatePlatform> platform =
55+
MultiIsolatePlatform::Create(4);
56+
V8::InitializePlatform(platform.get());
57+
V8::Initialize();
58+
59+
// See below for the contents of this function.
60+
int ret = RunNodeInstance(platform.get(), args, exec_args);
61+
62+
V8::Dispose();
63+
V8::ShutdownPlatform();
64+
return ret;
65+
}
66+
```
67+
68+
### Per-instance state
69+
70+
Node.js has a concept of a “Node.js instance”, that is commonly being referred
71+
to as `node::Environment`. Each `node::Environment` is associated with:
72+
73+
* Exactly one `v8::Isolate`, i.e. one JS Engine instance,
74+
* Exactly one `uv_loop_t`, i.e. one event loop, and
75+
* A number of `v8::Context`s, but exactly one main `v8::Context`.
76+
* One `node::IsolateData` instance that contains information that could be
77+
shared by multiple `node::Environment`s that use the same `v8::Isolate`.
78+
Currently, no testing if performed for this scenario.
79+
80+
In order to set up a `v8::Isolate`, an `v8::ArrayBuffer::Allocator` needs
81+
to be provided. One possible choice is the default Node.js allocator, which
82+
can be created through `node::ArrayBufferAllocator::Create()`. Using the Node.js
83+
allocator allows minor performance optimizations when addons use the Node.js
84+
C++ `Buffer` API, and is required in order to track `ArrayBuffer` memory in
85+
[`process.memoryUsage()`][].
86+
87+
Additionally, each `v8::Isolate` that is used for a Node.js instance needs to
88+
be registered and unregistered with the `MultiIsolatePlatform` instance, if one
89+
is being used, in order for the platform to know which event loop to use
90+
for tasks scheduled by the `v8::Isolate`.
91+
92+
The `node::NewIsolate()` helper function creates a `v8::Isolate`,
93+
sets it up with some Node.js-specific hooks (e.g. the Node.js error handler),
94+
and registers it with the platform automatically.
95+
96+
```cpp
97+
int RunNodeInstance(MultiIsolatePlatform* platform,
98+
const std::vector<std::string>& args,
99+
const std::vector<std::string>& exec_args) {
100+
int exit_code = 0;
101+
// Set up a libuv event loop.
102+
uv_loop_t loop;
103+
int ret = uv_loop_init(&loop);
104+
if (ret != 0) {
105+
fprintf(stderr, "%s: Failed to initialize loop: %s\n",
106+
args[0].c_str(),
107+
uv_err_name(ret));
108+
return 1;
109+
}
110+
111+
std::shared_ptr<ArrayBufferAllocator> allocator =
112+
ArrayBufferAllocator::Create();
113+
114+
Isolate* isolate = NewIsolate(allocator, &loop, platform);
115+
if (isolate == nullptr) {
116+
fprintf(stderr, "%s: Failed to initialize V8 Isolate\n", args[0].c_str());
117+
return 1;
118+
}
119+
120+
{
121+
Locker locker(isolate);
122+
Isolate::Scope isolate_scope(isolate);
123+
124+
// Create a node::IsolateData instance that will later be released using
125+
// node::FreeIsolateData().
126+
std::unique_ptr<IsolateData, decltype(&node::FreeIsolateData)> isolate_data(
127+
node::CreateIsolateData(isolate, &loop, platform, allocator.get()),
128+
node::FreeIsolateData);
129+
130+
// Set up a new v8::Context.
131+
HandleScope handle_scope(isolate);
132+
Local<Context> context = node::NewContext(isolate);
133+
if (context.IsEmpty()) {
134+
fprintf(stderr, "%s: Failed to initialize V8 Context\n", args[0].c_str());
135+
return 1;
136+
}
137+
138+
// The v8::Context needs to be entered when node::CreateEnvironment() and
139+
// node::LoadEnvironment() are being called.
140+
Context::Scope context_scope(context);
141+
142+
// Create a node::Environment instance that will later be released using
143+
// node::FreeEnvironment().
144+
std::unique_ptr<Environment, decltype(&node::FreeEnvironment)> env(
145+
node::CreateEnvironment(isolate_data.get(), context, args, exec_args),
146+
node::FreeEnvironment);
147+
148+
// Set up the Node.js instance for execution, and run code inside of it.
149+
// There is also a variant that takes a callback and provides it with
150+
// the `require` and `process` objects, so that it can manually compile
151+
// and run scripts as needed.
152+
// The `require` function inside this script does *not* access the file
153+
// system, and can only load built-in Node.js modules.
154+
// `module.createRequire()` is being used to create one that is able to
155+
// load files from the disk, and uses the standard CommonJS file loader
156+
// instead of the internal-only `require` function.
157+
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
158+
env.get(),
159+
"const publicRequire ="
160+
" require('module').createRequire(process.cwd() + '/');"
161+
"globalThis.require = publicRequire;"
162+
"require('vm').runInThisContext(process.argv[1]);");
163+
164+
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
165+
return 1;
166+
167+
{
168+
// SealHandleScope protects against handle leaks from callbacks.
169+
SealHandleScope seal(isolate);
170+
bool more;
171+
do {
172+
uv_run(&loop, UV_RUN_DEFAULT);
173+
174+
// V8 tasks on background threads may end up scheduling new tasks in the
175+
// foreground, which in turn can keep the event loop going. For example,
176+
// WebAssembly.compile() may do so.
177+
platform->DrainTasks(isolate);
178+
179+
// If there are new tasks, continue.
180+
more = uv_loop_alive(&loop);
181+
if (more) continue;
182+
183+
// node::EmitBeforeExit() is used to emit the 'beforeExit' event on
184+
// the `process` object.
185+
node::EmitBeforeExit(env.get());
186+
187+
// 'beforeExit' can also schedule new work that keeps the event loop
188+
// running.
189+
more = uv_loop_alive(&loop);
190+
} while (more == true);
191+
}
192+
193+
// node::EmitExit() returns the current exit code.
194+
exit_code = node::EmitExit(env.get());
195+
196+
// node::Stop() can be used to explicitly stop the event loop and keep
197+
// further JavaScript from running. It can be called from any thread,
198+
// and will act like worker.terminate() if called from another thread.
199+
node::Stop(env.get());
200+
}
201+
202+
// Unregister the Isolate with the platform and add a listener that is called
203+
// when the Platform is done cleaning up any state it had associated with
204+
// the Isolate.
205+
bool platform_finished = false;
206+
platform->AddIsolateFinishedCallback(isolate, [](void* data) {
207+
*static_cast<bool*>(data) = true;
208+
}, &platform_finished);
209+
platform->UnregisterIsolate(isolate);
210+
isolate->Dispose();
211+
212+
// Wait until the platform has cleaned up all relevant resources.
213+
while (!platform_finished)
214+
uv_run(&loop, UV_RUN_ONCE);
215+
int err = uv_loop_close(&loop);
216+
assert(err == 0);
217+
218+
return exit_code;
219+
}
220+
```
221+
222+
[`process.memoryUsage()`]: process.html#process_process_memoryusage
223+
[CLI options]: cli.html
224+
[deprecation policy]: deprecations.html
225+
[embedtest.cc]: https://github.com/nodejs/node/blob/master/test/embedding/embedtest.cc
226+
[src/node.h]: https://github.com/nodejs/node/blob/master/src/node.h
Collapse file

‎doc/api/index.md‎

Copy file name to clipboardExpand all lines: doc/api/index.md
+4-3Lines changed: 4 additions & 3 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
* [Assertion testing](assert.html)
1414
* [Async hooks](async_hooks.html)
1515
* [Buffer](buffer.html)
16-
* [C++ addons](addons.html)
17-
* [C/C++ addons with N-API](n-api.html)
18-
* [Child processes](child_process.html)
16+
* [C++ Addons](addons.html)
17+
* [C/C++ Addons with N-API](n-api.html)
18+
* [C++ Embedder API](embedding.html)
19+
* [Child Processes](child_process.html)
1920
* [Cluster](cluster.html)
2021
* [Command line options](cli.html)
2122
* [Console](console.html)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.