Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node-api: segregate nogc APIs from rest via type system #50060

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
139 changes: 103 additions & 36 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ it still gets the benefits of the ABI stability provided by the C API.
When using `node-addon-api` instead of the C APIs, start with the API [docs][]
for `node-addon-api`.

The [Node-API Resource](https://nodejs.github.io/node-addon-examples/) offers
The [Node-API Resource](https://nodejs.github.io/node-addon-examples/) offers
an excellent orientation and tips for developers just getting started with
Node-API and `node-addon-api`. Additional media resources can be found on the
[Node-API Media][] page.
Expand Down Expand Up @@ -175,7 +175,8 @@ developers have run into limitations in node-gyp.
[CMake.js][] is an alternative build system based on [CMake][].

CMake.js is a good choice for projects that already use CMake or for
developers affected by limitations in node-gyp.
developers affected by limitations in node-gyp. [`build_with_cmake`][] is an
example of a CMake-based native addon project.

### Uploading precompiled binaries

Expand Down Expand Up @@ -237,6 +238,18 @@ Some of the Node-API surface is experimental and requires explicit opt-in:
In this case the entire API surface, including any experimental APIs, will be
available to the module code.

Occasionally, experimental features are introduced that affect already-released
and stable APIs. These features can be disabled by an opt-out:

```c
#define NAPI_EXPERIMENTAL
#define NODE_API_EXPERIMENTAL_<FEATURE_NAME>_OPT_OUT
#include <node_api.h>
```

where `<FEATURE_NAME>` is the name of an experimental feature that affects both
experimental and stable APIs.

## Node-API version matrix

Up until version 9, Node-API versions were additive and versioned
Expand Down Expand Up @@ -419,7 +432,7 @@ napi_value create_addon(napi_env env) {
#include <node_api.h>
#include "addon.h"

NAPI_MODULE_INIT() {
NAPI_MODULE_INIT(/* napi_env env, napi_value exports */) {
// This function body is expected to return a `napi_value`.
// The variables `napi_env env` and `napi_value exports` may be used within
// the body, as they are provided by the definition of `NAPI_MODULE_INIT()`.
Expand Down Expand Up @@ -464,7 +477,7 @@ napiVersion: 6
-->

```c
napi_status napi_set_instance_data(napi_env env,
napi_status napi_set_instance_data(node_api_nogc_env env,
void* data,
napi_finalize finalize_cb,
void* finalize_hint);
Expand Down Expand Up @@ -496,7 +509,7 @@ napiVersion: 6
-->

```c
napi_status napi_get_instance_data(napi_env env,
napi_status napi_get_instance_data(node_api_nogc_env env,
void** data);
```

Expand Down Expand Up @@ -598,6 +611,22 @@ when an instance of a native addon is unloaded. Notification of this event is
delivered through the callbacks given to [`napi_add_env_cleanup_hook`][] and
[`napi_set_instance_data`][].

### `node_api_nogc_env`

> Stability: 1 - Experimental

This variant of `napi_env` is passed to synchronous finalizers
([`node_api_nogc_finalize`][]). There is a subset of Node-APIs which accept
a parameter of type `node_api_nogc_env` as their first argument. These APIs do
not access the state of the JavaScript engine and are thus safe to call from
synchronous finalizers. Passing a parameter of type `napi_env` to these APIs is
allowed, however, passing a parameter of type `node_api_nogc_env` to APIs that
access the JavaScript engine state is not allowed. Attempting to do so without
a cast will produce a compiler warning or an error when add-ons are compiled
with flags which cause them to emit warnings and/or errors when incorrect
pointer types are passed into a function. Calling such APIs from a synchronous
finalizer will ultimately result in the termination of the application.

### `napi_value`

This is an opaque pointer that is used to represent a JavaScript value.
Expand Down Expand Up @@ -762,32 +791,36 @@ typedef napi_value (*napi_callback)(napi_env, napi_callback_info);
Unless for reasons discussed in [Object Lifetime Management][], creating a
handle and/or callback scope inside a `napi_callback` is not necessary.

#### `napi_finalize`
#### `node_api_nogc_finalize`

<!-- YAML
added: v8.0.0
napiVersion: 1
added: REPLACEME
-->

> Stability: 1 - Experimental

Function pointer type for add-on provided functions that allow the user to be
notified when externally-owned data is ready to be cleaned up because the
object with which it was associated with has been garbage-collected. The user
must provide a function satisfying the following signature which would get
called upon the object's collection. Currently, `napi_finalize` can be used for
object it was associated with has been garbage-collected. The user must provide
a function satisfying the following signature which would get called upon the
object's collection. Currently, `node_api_nogc_finalize` can be used for
finding out when objects that have external data are collected.

```c
typedef void (*napi_finalize)(napi_env env,
void* finalize_data,
void* finalize_hint);
typedef void (*node_api_nogc_finalize)(node_api_nogc_env env,
void* finalize_data,
void* finalize_hint);
```

Unless for reasons discussed in [Object Lifetime Management][], creating a
handle and/or callback scope inside the function body is not necessary.

Since these functions may be called while the JavaScript engine is in a state
where it cannot execute JavaScript code, some Node-API calls may return
`napi_pending_exception` even when there is no exception pending.
where it cannot execute JavaScript code, only Node-APIs which accept a
`node_api_nogc_env` as their first parameter may be called.
[`node_api_post_finalizer`][] can be used to schedule Node-API calls that
require access to the JavaScript engine's state to run after the current
garbage collection cycle has completed.

In the case of [`node_api_create_external_string_latin1`][] and
[`node_api_create_external_string_utf16`][] the `env` parameter may be null,
Expand All @@ -796,11 +829,39 @@ shutdown.

Change History:

* experimental (`NAPI_EXPERIMENTAL`):

Only Node-API calls that accept a `node_api_nogc_env` as their first
parameter may be called, otherwise the application will be terminated with an
appropriate error message. This feature can be turned off by defining
`NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT`.

#### `napi_finalize`

<!-- YAML
added: v8.0.0
napiVersion: 1
-->

Function pointer type for add-on provided function that allow the user to
schedule a group of calls to Node-APIs in response to a garbage collection
event, after the garbage collection cycle has completed. These function
pointers can be used with [`node_api_post_finalizer`][].

```c
typedef void (*napi_finalize)(napi_env env,
void* finalize_data,
void* finalize_hint);
```

Change History:

* experimental (`NAPI_EXPERIMENTAL` is defined):

Node-API calls made from a finalizer will return `napi_cannot_run_js` when
the JavaScript engine is unable to execute JavaScript, and will return
`napi_exception_pending` if there is a pending exception.
A function of this type may no longer be used as a finalizer, except with
[`node_api_post_finalizer`][]. [`node_api_nogc_finalize`][] must be used
instead. This feature can be turned off by defining
`NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT`.

#### `napi_async_execute_callback`

Expand Down Expand Up @@ -1002,7 +1063,7 @@ napiVersion: 1

```c
napi_status
napi_get_last_error_info(napi_env env,
napi_get_last_error_info(node_api_nogc_env env,
const napi_extended_error_info** result);
```

Expand Down Expand Up @@ -1821,7 +1882,7 @@ napiVersion: 3
-->

```c
NODE_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env,
NODE_EXTERN napi_status napi_add_env_cleanup_hook(node_api_nogc_env env,
napi_cleanup_hook fun,
void* arg);
```
Expand Down Expand Up @@ -1851,7 +1912,7 @@ napiVersion: 3
-->

```c
NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env,
NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(node_api_nogc_env env,
void (*fun)(void* arg),
void* arg);
```
Expand Down Expand Up @@ -1880,7 +1941,7 @@ changes:

```c
NAPI_EXTERN napi_status napi_add_async_cleanup_hook(
napi_env env,
node_api_nogc_env env,
napi_async_cleanup_hook hook,
void* arg,
napi_async_cleanup_hook_handle* remove_handle);
Expand Down Expand Up @@ -2038,7 +2099,7 @@ You can also use the `NAPI_MODULE_INIT` macro, which acts as a shorthand
for `NAPI_MODULE` and defining an `Init` function:

```c
NAPI_MODULE_INIT() {
NAPI_MODULE_INIT(/* napi_env env, napi_value exports */) {
napi_value answer;
napi_status result;

Expand All @@ -2052,6 +2113,9 @@ NAPI_MODULE_INIT() {
}
```

The parameters `env` and `exports` are provided to the body of the
`NAPI_MODULE_INIT` macro.

All Node-API addons are context-aware, meaning they may be loaded multiple
times. There are a few design considerations when declaring such a module.
The documentation on [context-aware addons][] provides more details.
Expand Down Expand Up @@ -5420,7 +5484,7 @@ napiVersion: 5
napi_status napi_add_finalizer(napi_env env,
napi_value js_object,
void* finalize_data,
napi_finalize finalize_cb,
node_api_nogc_finalize finalize_cb,
void* finalize_hint,
napi_ref* result);
```
Expand Down Expand Up @@ -5461,7 +5525,7 @@ added:
> Stability: 1 - Experimental

```c
napi_status node_api_post_finalizer(napi_env env,
napi_status node_api_post_finalizer(node_api_nogc_env env,
napi_finalize finalize_cb,
void* finalize_data,
void* finalize_hint);
Expand Down Expand Up @@ -5531,7 +5595,7 @@ Once created the async worker can be queued
for execution using the [`napi_queue_async_work`][] function:

```c
napi_status napi_queue_async_work(napi_env env,
napi_status napi_queue_async_work(node_api_nogc_env env,
napi_async_work work);
```

Expand Down Expand Up @@ -5623,7 +5687,7 @@ napiVersion: 1
-->

```c
napi_status napi_queue_async_work(napi_env env,
napi_status napi_queue_async_work(node_api_nogc_env env,
napi_async_work work);
```

Expand All @@ -5644,7 +5708,7 @@ napiVersion: 1
-->

```c
napi_status napi_cancel_async_work(napi_env env,
napi_status napi_cancel_async_work(node_api_nogc_env env,
napi_async_work work);
```

Expand Down Expand Up @@ -5848,7 +5912,7 @@ typedef struct {
const char* release;
} napi_node_version;

napi_status napi_get_node_version(napi_env env,
napi_status napi_get_node_version(node_api_nogc_env env,
const napi_node_version** version);
```

Expand All @@ -5871,7 +5935,7 @@ napiVersion: 1
-->

```c
napi_status napi_get_version(napi_env env,
napi_status napi_get_version(node_api_nogc_env env,
uint32_t* result);
```

Expand Down Expand Up @@ -5904,7 +5968,7 @@ napiVersion: 1
-->

```c
NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env,
NAPI_EXTERN napi_status napi_adjust_external_memory(node_api_nogc_env env,
int64_t change_in_bytes,
int64_t* result);
```
Expand Down Expand Up @@ -6121,7 +6185,7 @@ napiVersion: 2
-->

```c
NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
NAPI_EXTERN napi_status napi_get_uv_event_loop(node_api_nogc_env env,
struct uv_loop_s** loop);
```

Expand Down Expand Up @@ -6435,7 +6499,7 @@ napiVersion: 4

```c
NAPI_EXTERN napi_status
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func);
napi_ref_threadsafe_function(node_api_nogc_env env, napi_threadsafe_function func);
```

* `[in] env`: The environment that the API is invoked under.
Expand All @@ -6461,7 +6525,7 @@ napiVersion: 4

```c
NAPI_EXTERN napi_status
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func);
napi_unref_threadsafe_function(node_api_nogc_env env, napi_threadsafe_function func);
```

* `[in] env`: The environment that the API is invoked under.
Expand All @@ -6487,7 +6551,7 @@ napiVersion: 9

```c
NAPI_EXTERN napi_status
node_api_get_module_file_name(napi_env env, const char** result);
node_api_get_module_file_name(node_api_nogc_env env, const char** result);

```

Expand Down Expand Up @@ -6551,6 +6615,7 @@ the add-on's file name during loading.
[`Number.MIN_SAFE_INTEGER`]: https://tc39.github.io/ecma262/#sec-number.min_safe_integer
[`Worker`]: worker_threads.md#class-worker
[`async_hooks.executionAsyncResource()`]: async_hooks.md#async_hooksexecutionasyncresource
[`build_with_cmake`]: https://github.com/nodejs/node-addon-examples/tree/main/build_with_cmake
[`global`]: globals.md#global
[`init` hooks]: async_hooks.md#initasyncid-type-triggerasyncid-resource
[`napi_add_async_cleanup_hook`]: #napi_add_async_cleanup_hook
Expand Down Expand Up @@ -6614,6 +6679,8 @@ the add-on's file name during loading.
[`node_api_create_external_string_latin1`]: #node_api_create_external_string_latin1
[`node_api_create_external_string_utf16`]: #node_api_create_external_string_utf16
[`node_api_create_syntax_error`]: #node_api_create_syntax_error
[`node_api_nogc_finalize`]: #node_api_nogc_finalize
[`node_api_post_finalizer`]: #node_api_post_finalizer
[`node_api_throw_syntax_error`]: #node_api_throw_syntax_error
[`process.release`]: process.md#processrelease
[`uv_ref`]: https://docs.libuv.org/en/v1.x/handle.html#c.uv_ref
Expand Down
13 changes: 13 additions & 0 deletions doc/contributing/adding-new-napi-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,16 @@ Node-API.
to the decision to take an API out of experimental status.
* The API **must** be implemented in a Node.js implementation with an
alternate VM.

Since the adoption of the policy whereby moving to a later version of Node-API
from an earlier version may entail rework of existing code, it is possible to
introduce modifications to already-released Node-APIs, as long as the
modifications affect neither the ABI nor the API of earlier versions. Such
modifications **must** be accompanied by an opt-out flag. This provides add-on
maintainers who take advantage of the initial compile-time flag to track
impending changes to Node-API with

* a quick fix to the breakage caused,
* a notification that such breakage is impending, and thus
* a buffer to adoption above and beyond the one provided by the initial
compile-time flag.