Skip to content

Commit

Permalink
node-api: add libnode support
Browse files Browse the repository at this point in the history
  • Loading branch information
mmomtchev committed Oct 10, 2022
1 parent 0d80e73 commit 33b170d
Show file tree
Hide file tree
Showing 27 changed files with 1,096 additions and 51 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ coverage-report-js:
cctest: all
@out/$(BUILDTYPE)/$@ --gtest_filter=$(GTEST_FILTER)
@out/$(BUILDTYPE)/embedtest "require('./test/embedding/test-embedding.js')"
@out/$(BUILDTYPE)/napi_embedding "require('./test/embedding/test-napi-embedding.js')"
@out/$(BUILDTYPE)/napi_modules ../../test/embedding/cjs.cjs ../../test/embedding/es6.mjs

.PHONY: list-gtests
list-gtests:
Expand Down Expand Up @@ -551,6 +553,8 @@ test-ci: | clear-stalled bench-addons-build build-addons build-js-native-api-tes
--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
$(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC)
out/Release/embedtest 'require("./test/embedding/test-embedding.js")'
out/Release/napi_embedding 'require("./test/embedding/test-napi-embedding.js")'
out/Release/napi_modules ../../test/embedding/cjs.cjs ../../test/embedding/es6.mjs
$(info Clean up any leftover processes, error if found.)
ps awwx | grep Release/node | grep -v grep | cat
@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
Expand Down
16 changes: 2 additions & 14 deletions deps/uv/src/unix/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,6 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) {

while (r != 0 && loop->stop_flag == 0) {
uv__update_time(loop);
uv__run_timers(loop);
ran_pending = uv__run_pending(loop);
uv__run_idle(loop);
uv__run_prepare(loop);
Expand All @@ -395,22 +394,11 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) {
*/
uv__metrics_update_idle_time(loop);

uv__run_timers(loop);

uv__run_check(loop);
uv__run_closing_handles(loop);

if (mode == UV_RUN_ONCE) {
/* UV_RUN_ONCE implies forward progress: at least one callback must have
* been invoked when it returns. uv__io_poll() can return without doing
* I/O (meaning: no callbacks) when its timeout expires - which means we
* have pending timers that satisfy the forward progress constraint.
*
* UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
* the check.
*/
uv__update_time(loop);
uv__run_timers(loop);
}

r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
Expand Down
39 changes: 39 additions & 0 deletions doc/api/embedding.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,47 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
}
```

## Node-API Embedding

<!--introduced_in=REPLACEME-->

As an alternative, an embedded Node.js can also be fully controlled through
Node-API. This API supports both C and C++ through [node-addon-api][].

An example can be found [in the Node.js source tree][napi_embedding.c].

```c
napi_platform platform;
napi_env env;
const char *main_script = "console.log('hello world')";

if (napi_create_platform(0, NULL, 0, NULL, NULL, 0, &platform) != napi_ok) {
fprintf(stderr, "Failed creating the platform\n");
return -1;
}

if (napi_create_environment(platform, NULL, main_script,
(napi_stdio){NULL, NULL, NULL}, &env) != napi_ok) {
fprintf(stderr, "Failed running JS\n");
return -1;
}

// Here you can interact with the environment through Node-API env

if (napi_destroy_environment(env, NULL) != napi_ok) {
return -1;
}

if (napi_destroy_platform(platform) != napi_ok) {
fprintf(stderr, "Failed destroying the platform\n");
return -1;
}
```
[CLI options]: cli.md
[`process.memoryUsage()`]: process.md#processmemoryusage
[deprecation policy]: deprecations.md
[embedtest.cc]: https://github.com/nodejs/node/blob/HEAD/test/embedding/embedtest.cc
[napi_embedding.c]: https://github.com/nodejs/node/blob/HEAD/test/embedding/napi_embedding.c
[node-addon-api]: https://github.com/nodejs/node-addon-api
[src/node.h]: https://github.com/nodejs/node/blob/HEAD/src/node.h
130 changes: 130 additions & 0 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6267,6 +6267,136 @@ idempotent.

This API may only be called from the main thread.

## Using embedded Node.js

### `napi_create_platform`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
napi_status napi_create_platform(int argc,
char** argv,
int exec_argc,
char** exec_argv,
char*** errors,
int thread_pool_size,
napi_platform* result);
```

* `[in] argc`: CLI argument count, pass 0 for autofilling.
* `[in] argv`: CLI arguments, pass NULL for autofilling.
* `[in] exec_argc`: Node.js CLI options count.
* `[in] exec_argv`: Node.js CLI options.
* `[in] errors`: If different than NULL, will receive an array of
strings that must be freed.
* `[in] thread_pool_size`: Thread pool size, 0 for automatic.
* `[out] result`: A `napi_platform` result.

This function must be called once to initialize V8 and Node.js when using as a
shared library.

### `napi_destroy_platform`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
napi_status napi_destroy_platform(napi_platform platform, int *exit_code);
```

* `[in] platform`: platform handle.
* `[out] exit_code`: if not NULL will receive the process exit code.

Destroy the Node.js / V8 processes.

### `napi_create_environment`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
napi_status napi_create_environment(napi_platform platform,
char*** errors,
const char* main_script,
napi_env* result);
```

* `[in] platform`: platform handle.
* `[in] errors`: If different than NULL, will receive an array of strings
that must be freed.
* `[in] main_script`: If different than NULL, custom JavaScript to run in
addition to the default bootstrap that creates an empty
ready-to-use CJS/ES6 environment with `global.require()` and
`global.import()` functions that resolve modules from the directory of
the compiled binary.
It can be used to redirect `process.stdin`/ `process.stdout` streams
since Node.js might switch these file descriptors to non-blocking mode.
* `[out] result`: A `napi_env` result.

Initialize a new environment. A single platform can hold multiple Node.js
environments that will run in a separate V8 isolate each. If the returned
value is `napi_ok` or `napi_pending_exception`, the environment must be
destroyed with `napi_destroy_environment` to free all allocated memory.

### `napi_run_environment`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
napi_status napi_run_environment(napi_env env);
```

### `napi_await_promise`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
napi_status napi_await_promise(napi_env env,
napi_value promise,
napi_value *result);
```

* `[in] env`: environment handle.
* `[in] promise`: JS Promise.
* `[out] result`: Will receive the value that the Promise resolved with.

Iterate the event loop of the environment until the `promise` has been
resolved. Returns `napi_pending_exception` on rejection.

### `napi_destroy_environment`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

```c
napi_status napi_destroy_environment(napi_env env);
```

* `[in] env`: environment handle.

Destroy the Node.js environment / V8 isolate.

## Miscellaneous utilities

### `node_api_get_module_file_name`
Expand Down
21 changes: 21 additions & 0 deletions lib/internal/bootstrap/switches/is_embedded_env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

// This is the bootstrapping code used when creating a new environment
// through N-API

// Set up globalThis.require and globalThis.import so that they can
// be easily accessed from C/C++

/* global path, primordials */

const { globalThis, ObjectCreate } = primordials;
const CJSLoader = require('internal/modules/cjs/loader');
const ESMLoader = require('internal/modules/esm/loader').ESMLoader;

globalThis.module = new CJSLoader.Module();
globalThis.require = require('module').createRequire(path);

const internalLoader = new ESMLoader();
const parent_path = require('url').pathToFileURL(path);
globalThis.import = (mod) => internalLoader.import(mod, parent_path, ObjectCreate(null));
globalThis.import.meta = { url: parent_path };
104 changes: 104 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,110 @@
],
}, # embedtest

{
'target_name': 'napi_embedding',
'type': 'executable',

'dependencies': [
'<(node_lib_target_name)',
'deps/histogram/histogram.gyp:histogram',
'deps/uvwasi/uvwasi.gyp:uvwasi',
],

'includes': [
'node.gypi'
],

'include_dirs': [
'src',
'tools/msvs/genfiles',
'deps/v8/include',
'deps/cares/include',
'deps/uv/include',
'deps/uvwasi/include',
'test/embedding',
],

'sources': [
'src/node_snapshot_stub.cc',
'test/embedding/napi_embedding.c',
],

'conditions': [
['OS=="solaris"', {
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
}],
# Skip cctest while building shared lib node for Windows
[ 'OS=="win" and node_shared=="true"', {
'type': 'none',
}],
[ 'node_shared=="true"', {
'xcode_settings': {
'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
},
}],
['OS=="win"', {
'libraries': [
'Dbghelp.lib',
'winmm.lib',
'Ws2_32.lib',
],
}],
],
}, # napi_embedding

{
'target_name': 'napi_modules',
'type': 'executable',

'dependencies': [
'<(node_lib_target_name)',
'deps/histogram/histogram.gyp:histogram',
'deps/uvwasi/uvwasi.gyp:uvwasi',
],

'includes': [
'node.gypi'
],

'include_dirs': [
'src',
'tools/msvs/genfiles',
'deps/v8/include',
'deps/cares/include',
'deps/uv/include',
'deps/uvwasi/include',
'test/embedding',
],

'sources': [
'src/node_snapshot_stub.cc',
'test/embedding/napi_modules.c',
],

'conditions': [
['OS=="solaris"', {
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
}],
# Skip cctest while building shared lib node for Windows
[ 'OS=="win" and node_shared=="true"', {
'type': 'none',
}],
[ 'node_shared=="true"', {
'xcode_settings': {
'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ],
},
}],
['OS=="win"', {
'libraries': [
'Dbghelp.lib',
'winmm.lib',
'Ws2_32.lib',
],
}],
],
}, # napi_modules

{
'target_name': 'overlapped-checker',
'type': 'executable',
Expand Down
Loading

0 comments on commit 33b170d

Please sign in to comment.