Skip to content

Commit 33b170d

Browse files
committed
node-api: add libnode support
1 parent 0d80e73 commit 33b170d

27 files changed

+1096
-51
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ coverage-report-js:
286286
cctest: all
287287
@out/$(BUILDTYPE)/$@ --gtest_filter=$(GTEST_FILTER)
288288
@out/$(BUILDTYPE)/embedtest "require('./test/embedding/test-embedding.js')"
289+
@out/$(BUILDTYPE)/napi_embedding "require('./test/embedding/test-napi-embedding.js')"
290+
@out/$(BUILDTYPE)/napi_modules ../../test/embedding/cjs.cjs ../../test/embedding/es6.mjs
289291

290292
.PHONY: list-gtests
291293
list-gtests:
@@ -551,6 +553,8 @@ test-ci: | clear-stalled bench-addons-build build-addons build-js-native-api-tes
551553
--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
552554
$(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC)
553555
out/Release/embedtest 'require("./test/embedding/test-embedding.js")'
556+
out/Release/napi_embedding 'require("./test/embedding/test-napi-embedding.js")'
557+
out/Release/napi_modules ../../test/embedding/cjs.cjs ../../test/embedding/es6.mjs
554558
$(info Clean up any leftover processes, error if found.)
555559
ps awwx | grep Release/node | grep -v grep | cat
556560
@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \

deps/uv/src/unix/core.c

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,6 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) {
377377

378378
while (r != 0 && loop->stop_flag == 0) {
379379
uv__update_time(loop);
380-
uv__run_timers(loop);
381380
ran_pending = uv__run_pending(loop);
382381
uv__run_idle(loop);
383382
uv__run_prepare(loop);
@@ -395,22 +394,11 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) {
395394
*/
396395
uv__metrics_update_idle_time(loop);
397396

397+
uv__run_timers(loop);
398+
398399
uv__run_check(loop);
399400
uv__run_closing_handles(loop);
400401

401-
if (mode == UV_RUN_ONCE) {
402-
/* UV_RUN_ONCE implies forward progress: at least one callback must have
403-
* been invoked when it returns. uv__io_poll() can return without doing
404-
* I/O (meaning: no callbacks) when its timeout expires - which means we
405-
* have pending timers that satisfy the forward progress constraint.
406-
*
407-
* UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
408-
* the check.
409-
*/
410-
uv__update_time(loop);
411-
uv__run_timers(loop);
412-
}
413-
414402
r = uv__loop_alive(loop);
415403
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
416404
break;

doc/api/embedding.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,47 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
166166
}
167167
```
168168

169+
## Node-API Embedding
170+
171+
<!--introduced_in=REPLACEME-->
172+
173+
As an alternative, an embedded Node.js can also be fully controlled through
174+
Node-API. This API supports both C and C++ through [node-addon-api][].
175+
176+
An example can be found [in the Node.js source tree][napi_embedding.c].
177+
178+
```c
179+
napi_platform platform;
180+
napi_env env;
181+
const char *main_script = "console.log('hello world')";
182+
183+
if (napi_create_platform(0, NULL, 0, NULL, NULL, 0, &platform) != napi_ok) {
184+
fprintf(stderr, "Failed creating the platform\n");
185+
return -1;
186+
}
187+
188+
if (napi_create_environment(platform, NULL, main_script,
189+
(napi_stdio){NULL, NULL, NULL}, &env) != napi_ok) {
190+
fprintf(stderr, "Failed running JS\n");
191+
return -1;
192+
}
193+
194+
// Here you can interact with the environment through Node-API env
195+
196+
if (napi_destroy_environment(env, NULL) != napi_ok) {
197+
return -1;
198+
}
199+
200+
if (napi_destroy_platform(platform) != napi_ok) {
201+
fprintf(stderr, "Failed destroying the platform\n");
202+
return -1;
203+
}
204+
```
205+
169206
[CLI options]: cli.md
170207
[`process.memoryUsage()`]: process.md#processmemoryusage
171208
[deprecation policy]: deprecations.md
172209
[embedtest.cc]: https://github.com/nodejs/node/blob/HEAD/test/embedding/embedtest.cc
210+
[napi_embedding.c]: https://github.com/nodejs/node/blob/HEAD/test/embedding/napi_embedding.c
211+
[node-addon-api]: https://github.com/nodejs/node-addon-api
173212
[src/node.h]: https://github.com/nodejs/node/blob/HEAD/src/node.h

doc/api/n-api.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6267,6 +6267,136 @@ idempotent.
62676267

62686268
This API may only be called from the main thread.
62696269

6270+
## Using embedded Node.js
6271+
6272+
### `napi_create_platform`
6273+
6274+
<!-- YAML
6275+
added: REPLACEME
6276+
-->
6277+
6278+
> Stability: 1 - Experimental
6279+
6280+
```c
6281+
napi_status napi_create_platform(int argc,
6282+
char** argv,
6283+
int exec_argc,
6284+
char** exec_argv,
6285+
char*** errors,
6286+
int thread_pool_size,
6287+
napi_platform* result);
6288+
```
6289+
6290+
* `[in] argc`: CLI argument count, pass 0 for autofilling.
6291+
* `[in] argv`: CLI arguments, pass NULL for autofilling.
6292+
* `[in] exec_argc`: Node.js CLI options count.
6293+
* `[in] exec_argv`: Node.js CLI options.
6294+
* `[in] errors`: If different than NULL, will receive an array of
6295+
strings that must be freed.
6296+
* `[in] thread_pool_size`: Thread pool size, 0 for automatic.
6297+
* `[out] result`: A `napi_platform` result.
6298+
6299+
This function must be called once to initialize V8 and Node.js when using as a
6300+
shared library.
6301+
6302+
### `napi_destroy_platform`
6303+
6304+
<!-- YAML
6305+
added: REPLACEME
6306+
-->
6307+
6308+
> Stability: 1 - Experimental
6309+
6310+
```c
6311+
napi_status napi_destroy_platform(napi_platform platform, int *exit_code);
6312+
```
6313+
6314+
* `[in] platform`: platform handle.
6315+
* `[out] exit_code`: if not NULL will receive the process exit code.
6316+
6317+
Destroy the Node.js / V8 processes.
6318+
6319+
### `napi_create_environment`
6320+
6321+
<!-- YAML
6322+
added: REPLACEME
6323+
-->
6324+
6325+
> Stability: 1 - Experimental
6326+
6327+
```c
6328+
napi_status napi_create_environment(napi_platform platform,
6329+
char*** errors,
6330+
const char* main_script,
6331+
napi_env* result);
6332+
```
6333+
6334+
* `[in] platform`: platform handle.
6335+
* `[in] errors`: If different than NULL, will receive an array of strings
6336+
that must be freed.
6337+
* `[in] main_script`: If different than NULL, custom JavaScript to run in
6338+
addition to the default bootstrap that creates an empty
6339+
ready-to-use CJS/ES6 environment with `global.require()` and
6340+
`global.import()` functions that resolve modules from the directory of
6341+
the compiled binary.
6342+
It can be used to redirect `process.stdin`/ `process.stdout` streams
6343+
since Node.js might switch these file descriptors to non-blocking mode.
6344+
* `[out] result`: A `napi_env` result.
6345+
6346+
Initialize a new environment. A single platform can hold multiple Node.js
6347+
environments that will run in a separate V8 isolate each. If the returned
6348+
value is `napi_ok` or `napi_pending_exception`, the environment must be
6349+
destroyed with `napi_destroy_environment` to free all allocated memory.
6350+
6351+
### `napi_run_environment`
6352+
6353+
<!-- YAML
6354+
added: REPLACEME
6355+
-->
6356+
6357+
> Stability: 1 - Experimental
6358+
6359+
```c
6360+
napi_status napi_run_environment(napi_env env);
6361+
```
6362+
6363+
### `napi_await_promise`
6364+
6365+
<!-- YAML
6366+
added: REPLACEME
6367+
-->
6368+
6369+
> Stability: 1 - Experimental
6370+
6371+
```c
6372+
napi_status napi_await_promise(napi_env env,
6373+
napi_value promise,
6374+
napi_value *result);
6375+
```
6376+
6377+
* `[in] env`: environment handle.
6378+
* `[in] promise`: JS Promise.
6379+
* `[out] result`: Will receive the value that the Promise resolved with.
6380+
6381+
Iterate the event loop of the environment until the `promise` has been
6382+
resolved. Returns `napi_pending_exception` on rejection.
6383+
6384+
### `napi_destroy_environment`
6385+
6386+
<!-- YAML
6387+
added: REPLACEME
6388+
-->
6389+
6390+
> Stability: 1 - Experimental
6391+
6392+
```c
6393+
napi_status napi_destroy_environment(napi_env env);
6394+
```
6395+
6396+
* `[in] env`: environment handle.
6397+
6398+
Destroy the Node.js environment / V8 isolate.
6399+
62706400
## Miscellaneous utilities
62716401

62726402
### `node_api_get_module_file_name`
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
3+
// This is the bootstrapping code used when creating a new environment
4+
// through N-API
5+
6+
// Set up globalThis.require and globalThis.import so that they can
7+
// be easily accessed from C/C++
8+
9+
/* global path, primordials */
10+
11+
const { globalThis, ObjectCreate } = primordials;
12+
const CJSLoader = require('internal/modules/cjs/loader');
13+
const ESMLoader = require('internal/modules/esm/loader').ESMLoader;
14+
15+
globalThis.module = new CJSLoader.Module();
16+
globalThis.require = require('module').createRequire(path);
17+
18+
const internalLoader = new ESMLoader();
19+
const parent_path = require('url').pathToFileURL(path);
20+
globalThis.import = (mod) => internalLoader.import(mod, parent_path, ObjectCreate(null));
21+
globalThis.import.meta = { url: parent_path };

node.gyp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,110 @@
11081108
],
11091109
}, # embedtest
11101110

1111+
{
1112+
'target_name': 'napi_embedding',
1113+
'type': 'executable',
1114+
1115+
'dependencies': [
1116+
'<(node_lib_target_name)',
1117+
'deps/histogram/histogram.gyp:histogram',
1118+
'deps/uvwasi/uvwasi.gyp:uvwasi',
1119+
],
1120+
1121+
'includes': [
1122+
'node.gypi'
1123+
],
1124+
1125+
'include_dirs': [
1126+
'src',
1127+
'tools/msvs/genfiles',
1128+
'deps/v8/include',
1129+
'deps/cares/include',
1130+
'deps/uv/include',
1131+
'deps/uvwasi/include',
1132+
'test/embedding',
1133+
],
1134+
1135+
'sources': [
1136+
'src/node_snapshot_stub.cc',
1137+
'test/embedding/napi_embedding.c',
1138+
],
1139+
1140+
'conditions': [
1141+
['OS=="solaris"', {
1142+
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
1143+
}],
1144+
# Skip cctest while building shared lib node for Windows
1145+
[ 'OS=="win" and node_shared=="true"', {
1146+
'type': 'none',
1147+
}],
1148+
[ 'node_shared=="true"', {
1149+
'xcode_settings': {
1150+
'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
1151+
},
1152+
}],
1153+
['OS=="win"', {
1154+
'libraries': [
1155+
'Dbghelp.lib',
1156+
'winmm.lib',
1157+
'Ws2_32.lib',
1158+
],
1159+
}],
1160+
],
1161+
}, # napi_embedding
1162+
1163+
{
1164+
'target_name': 'napi_modules',
1165+
'type': 'executable',
1166+
1167+
'dependencies': [
1168+
'<(node_lib_target_name)',
1169+
'deps/histogram/histogram.gyp:histogram',
1170+
'deps/uvwasi/uvwasi.gyp:uvwasi',
1171+
],
1172+
1173+
'includes': [
1174+
'node.gypi'
1175+
],
1176+
1177+
'include_dirs': [
1178+
'src',
1179+
'tools/msvs/genfiles',
1180+
'deps/v8/include',
1181+
'deps/cares/include',
1182+
'deps/uv/include',
1183+
'deps/uvwasi/include',
1184+
'test/embedding',
1185+
],
1186+
1187+
'sources': [
1188+
'src/node_snapshot_stub.cc',
1189+
'test/embedding/napi_modules.c',
1190+
],
1191+
1192+
'conditions': [
1193+
['OS=="solaris"', {
1194+
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
1195+
}],
1196+
# Skip cctest while building shared lib node for Windows
1197+
[ 'OS=="win" and node_shared=="true"', {
1198+
'type': 'none',
1199+
}],
1200+
[ 'node_shared=="true"', {
1201+
'xcode_settings': {
1202+
'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
1203+
},
1204+
}],
1205+
['OS=="win"', {
1206+
'libraries': [
1207+
'Dbghelp.lib',
1208+
'winmm.lib',
1209+
'Ws2_32.lib',
1210+
],
1211+
}],
1212+
],
1213+
}, # napi_modules
1214+
11111215
{
11121216
'target_name': 'overlapped-checker',
11131217
'type': 'executable',

0 commit comments

Comments
 (0)