Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
node-api: handle no support for external buffers
Refs: electron/electron#35801
Refs: nodejs/abi-stable-node#441

Electron recently dropped support for external
buffers. Provide a way for addon authors to:
- hide the methods to create external buffers so they can
  avoid using them if they want the broadest compatibility.
- call the methods that create external buffers at runtime
  to check if external buffers are supported and either
  use them or not based on the return code.

Signed-off-by: Michael Dawson <mdawson@devrus.com>

PR-URL: #45181
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
  • Loading branch information
mhdawson authored and danielleadams committed Jan 3, 2023
1 parent 04f213b commit 746175a
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 2 deletions.
27 changes: 27 additions & 0 deletions doc/api/n-api.md
Expand Up @@ -579,6 +579,7 @@ typedef enum {
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock, /* unused */
napi_no_external_buffers_allowed
} napi_status;
```

Expand Down Expand Up @@ -2403,6 +2404,19 @@ napi_create_external_arraybuffer(napi_env env,

Returns `napi_ok` if the API succeeded.

**Some runtimes other than Node.js have dropped support for external buffers**.
On runtimes other than Node.js this method may return
`napi_no_external_buffers_allowed` to indicate that external
buffers are not supported. One such runtime is Electron as
described in this issue
[electron/issues/35801](https://github.com/electron/electron/issues/35801).

In order to maintain broadest compatibility with all runtimes
you may define `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` in your addon before
includes for the node-api headers. Doing so will hide the 2 functions
that create external buffers. This will ensure a compilation error
occurs if you accidentally use one of these methods.

This API returns a Node-API value corresponding to a JavaScript `ArrayBuffer`.
The underlying byte buffer of the `ArrayBuffer` is externally allocated and
managed. The caller must ensure that the byte buffer remains valid until the
Expand Down Expand Up @@ -2447,6 +2461,19 @@ napi_status napi_create_external_buffer(napi_env env,

Returns `napi_ok` if the API succeeded.

**Some runtimes other than Node.js have dropped support for external buffers**.
On runtimes other than Node.js this method may return
`napi_no_external_buffers_allowed` to indicate that external
buffers are not supported. One such runtime is Electron as
described in this issue
[electron/issues/35801](https://github.com/electron/electron/issues/35801).

In order to maintain broadest compatibility with all runtimes
you may define `NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED` in your addon before
includes for the node-api headers. Doing so will hide the 2 functions
that create external buffers. This will ensure a compilation error
occurs if you accidentally use one of these methods.

This API allocates a `node::Buffer` object and initializes it with data
backed by the passed in buffer. While this is still a fully-supported data
structure, in most cases using a `TypedArray` will suffice.
Expand Down
2 changes: 2 additions & 0 deletions src/js_native_api.h
Expand Up @@ -401,13 +401,15 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env,
size_t byte_length,
void** data,
napi_value* result);
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
NAPI_EXTERN napi_status NAPI_CDECL
napi_create_external_arraybuffer(napi_env env,
void* external_data,
size_t byte_length,
napi_finalize finalize_cb,
void* finalize_hint,
napi_value* result);
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
NAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info(
napi_env env, napi_value arraybuffer, void** data, size_t* byte_length);
NAPI_EXTERN napi_status NAPI_CDECL napi_is_typedarray(napi_env env,
Expand Down
3 changes: 2 additions & 1 deletion src/js_native_api_types.h
Expand Up @@ -98,7 +98,8 @@ typedef enum {
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock // unused
napi_would_deadlock, // unused
napi_no_external_buffers_allowed
} napi_status;
// Note: when adding a new enum value to `napi_status`, please also update
// * `const int last_status` in the definition of `napi_get_last_error_info()'
Expand Down
3 changes: 2 additions & 1 deletion src/js_native_api_v8.cc
Expand Up @@ -746,6 +746,7 @@ static const char* error_messages[] = {
"An arraybuffer was expected",
"A detachable arraybuffer was expected",
"Main thread would deadlock",
"External buffers are not allowed",
};

napi_status NAPI_CDECL napi_get_last_error_info(
Expand All @@ -757,7 +758,7 @@ napi_status NAPI_CDECL napi_get_last_error_info(
// message in the `napi_status` enum each time a new error message is added.
// We don't have a napi_status_last as this would result in an ABI
// change each time a message was added.
const int last_status = napi_would_deadlock;
const int last_status = napi_no_external_buffers_allowed;

static_assert(NAPI_ARRAYSIZE(error_messages) == last_status + 1,
"Count of error messages must match count of error values");
Expand Down
4 changes: 4 additions & 0 deletions src/node_api.cc
Expand Up @@ -950,6 +950,10 @@ napi_status NAPI_CDECL napi_create_external_buffer(napi_env env,
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);

#if defined(V8_ENABLE_SANDBOX)
return napi_set_last_error(env, napi_no_external_buffers_allowed);
#endif

v8::Isolate* isolate = env->isolate;

// The finalizer object will delete itself after invoking the callback.
Expand Down
2 changes: 2 additions & 0 deletions src/node_api.h
Expand Up @@ -153,13 +153,15 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer(napi_env env,
size_t length,
void** data,
napi_value* result);
#ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
NAPI_EXTERN napi_status NAPI_CDECL
napi_create_external_buffer(napi_env env,
size_t length,
void* data,
napi_finalize finalize_cb,
void* finalize_hint,
napi_value* result);
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
NAPI_EXTERN napi_status NAPI_CDECL napi_create_buffer_copy(napi_env env,
size_t length,
const void* data,
Expand Down
5 changes: 5 additions & 0 deletions test/js-native-api/test_general/test_general.c
@@ -1,3 +1,8 @@
// we define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED here to
// validate that it can be used as a form of test itself. It is
// not related to any of the other tests
// defined in the file
#define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
Expand Down
5 changes: 5 additions & 0 deletions test/node-api/test_general/test_general.c
@@ -1,4 +1,9 @@
#define NAPI_EXPERIMENTAL
// we define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED here to validate that it can
// be used as a form of test itself. It is
// not related to any of the other tests
// defined in the file
#define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
#include <node_api.h>
#include <stdlib.h>
#include "../../js-native-api/common.h"
Expand Down

0 comments on commit 746175a

Please sign in to comment.