node-api: added SharedArrayBuffer api

PR-URL: https://github.com/nodejs/node/pull/59071
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
Mert Can Altin 2025-09-16 12:57:06 +03:00 committed by GitHub
parent 7c2cb67141
commit f1a8f447d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 334 additions and 13 deletions

View File

@ -3299,6 +3299,10 @@ Specification.
<!-- YAML
added: v8.0.0
napiVersion: 1
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/59071
description: Added support for `SharedArrayBuffer`.
-->
```c
@ -3309,21 +3313,20 @@ napi_status napi_get_arraybuffer_info(napi_env env,
```
* `[in] env`: The environment that the API is invoked under.
* `[in] arraybuffer`: `napi_value` representing the `ArrayBuffer` being queried.
* `[out] data`: The underlying data buffer of the `ArrayBuffer`. If byte\_length
* `[in] arraybuffer`: `napi_value` representing the `ArrayBuffer` or `SharedArrayBuffer` being queried.
* `[out] data`: The underlying data buffer of the `ArrayBuffer` or `SharedArrayBuffer`
is `0`, this may be `NULL` or any other pointer value.
* `[out] byte_length`: Length in bytes of the underlying data buffer.
Returns `napi_ok` if the API succeeded.
This API is used to retrieve the underlying data buffer of an `ArrayBuffer` and
its length.
This API is used to retrieve the underlying data buffer of an `ArrayBuffer` or `SharedArrayBuffer` and its length.
_WARNING_: Use caution while using this API. The lifetime of the underlying data
buffer is managed by the `ArrayBuffer` even after it's returned. A
buffer is managed by the `ArrayBuffer` or `SharedArrayBuffer` even after it's returned. A
possible safe way to use this API is in conjunction with
[`napi_create_reference`][], which can be used to guarantee control over the
lifetime of the `ArrayBuffer`. It's also safe to use the returned data buffer
lifetime of the `ArrayBuffer` or `SharedArrayBuffer`. It's also safe to use the returned data buffer
within the same callback as long as there are no calls to other APIs that might
trigger a GC.
@ -4278,6 +4281,63 @@ This API represents the invocation of the `ArrayBuffer` `IsDetachedBuffer`
operation as defined in [Section isDetachedBuffer][] of the ECMAScript Language
Specification.
### `node_api_is_sharedarraybuffer`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental
```c
napi_status node_api_is_sharedarraybuffer(napi_env env, napi_value value, bool* result)
```
* `[in] env`: The environment that the API is invoked under.
* `[in] value`: The JavaScript value to check.
* `[out] result`: Whether the given `napi_value` represents a `SharedArrayBuffer`.
Returns `napi_ok` if the API succeeded.
This API checks if the Object passed in is a `SharedArrayBuffer`.
### `node_api_create_sharedarraybuffer`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental
```c
napi_status node_api_create_sharedarraybuffer(napi_env env,
size_t byte_length,
void** data,
napi_value* result)
```
* `[in] env`: The environment that the API is invoked under.
* `[in] byte_length`: The length in bytes of the shared array buffer to create.
* `[out] data`: Pointer to the underlying byte buffer of the `SharedArrayBuffer`.
`data` can optionally be ignored by passing `NULL`.
* `[out] result`: A `napi_value` representing a JavaScript `SharedArrayBuffer`.
Returns `napi_ok` if the API succeeded.
This API returns a Node-API value corresponding to a JavaScript `SharedArrayBuffer`.
`SharedArrayBuffer`s are used to represent fixed-length binary data buffers that
can be shared across multiple workers.
The `SharedArrayBuffer` allocated will have an underlying byte buffer whose size is
determined by the `byte_length` parameter that's passed in.
The underlying buffer is optionally returned back to the caller in case the
caller wants to directly manipulate the buffer. This buffer can only be
written to directly from native code. To write to this buffer from JavaScript,
a typed array or `DataView` object would need to be created.
JavaScript `SharedArrayBuffer` objects are described in
[Section SharedArrayBuffer objects][] of the ECMAScript Language Specification.
## Working with JavaScript properties
Node-API exposes a set of APIs to get and set properties on JavaScript
@ -6791,6 +6851,7 @@ the add-on's file name during loading.
[Section IsArray]: https://tc39.es/ecma262/#sec-isarray
[Section IsStrctEqual]: https://tc39.es/ecma262/#sec-strict-equality-comparison
[Section Promise objects]: https://tc39.es/ecma262/#sec-promise-objects
[Section SharedArrayBuffer objects]: https://tc39.es/ecma262/#sec-sharedarraybuffer-objects
[Section ToBoolean]: https://tc39.es/ecma262/#sec-toboolean
[Section ToNumber]: https://tc39.es/ecma262/#sec-tonumber
[Section ToObject]: https://tc39.es/ecma262/#sec-toobject

View File

@ -486,6 +486,14 @@ napi_get_dataview_info(napi_env env,
napi_value* arraybuffer,
size_t* byte_offset);
#ifdef NAPI_EXPERIMENTAL
#define NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER
NAPI_EXTERN napi_status NAPI_CDECL
node_api_is_sharedarraybuffer(napi_env env, napi_value value, bool* result);
NAPI_EXTERN napi_status NAPI_CDECL node_api_create_sharedarraybuffer(
napi_env env, size_t byte_length, void** data, napi_value* result);
#endif // NAPI_EXPERIMENTAL
// version management
NAPI_EXTERN napi_status NAPI_CDECL napi_get_version(node_api_basic_env env,
uint32_t* result);

View File

@ -3077,21 +3077,68 @@ napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env,
CHECK_ARG(env, arraybuffer);
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
v8::Local<v8::ArrayBuffer> ab = value.As<v8::ArrayBuffer>();
if (value->IsArrayBuffer()) {
v8::Local<v8::ArrayBuffer> ab = value.As<v8::ArrayBuffer>();
if (data != nullptr) {
*data = ab->Data();
}
if (data != nullptr) {
*data = ab->Data();
}
if (byte_length != nullptr) {
*byte_length = ab->ByteLength();
if (byte_length != nullptr) {
*byte_length = ab->ByteLength();
}
} else if (value->IsSharedArrayBuffer()) {
v8::Local<v8::SharedArrayBuffer> sab = value.As<v8::SharedArrayBuffer>();
if (data != nullptr) {
*data = sab->Data();
}
if (byte_length != nullptr) {
*byte_length = sab->ByteLength();
}
} else {
return napi_set_last_error(env, napi_invalid_arg);
}
return napi_clear_last_error(env);
}
napi_status NAPI_CDECL node_api_is_sharedarraybuffer(napi_env env,
napi_value value,
bool* result) {
CHECK_ENV_NOT_IN_GC(env);
CHECK_ARG(env, value);
CHECK_ARG(env, result);
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
*result = val->IsSharedArrayBuffer();
return napi_clear_last_error(env);
}
napi_status NAPI_CDECL node_api_create_sharedarraybuffer(napi_env env,
size_t byte_length,
void** data,
napi_value* result) {
NAPI_PREAMBLE(env);
CHECK_ARG(env, result);
v8::Isolate* isolate = env->isolate;
v8::Local<v8::SharedArrayBuffer> buffer =
v8::SharedArrayBuffer::New(isolate, byte_length);
// Optionally return a pointer to the buffer's data, to avoid another call to
// retrieve it.
if (data != nullptr) {
*data = buffer->Data();
}
*result = v8impl::JsValueFromV8LocalValue(buffer);
return GET_RETURN_STATUS(env);
}
napi_status NAPI_CDECL napi_is_typedarray(napi_env env,
napi_value value,
bool* result) {

View File

@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "test_sharedarraybuffer",
"sources": [ "test_sharedarraybuffer.c" ]
}
]
}

View File

@ -0,0 +1,67 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const test_sharedarraybuffer = require(`./build/${common.buildType}/test_sharedarraybuffer`);
{
const sab = new SharedArrayBuffer(16);
const ab = new ArrayBuffer(16);
const obj = {};
const arr = [];
assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(sab), true);
assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(ab), false);
assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(obj), false);
assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(arr), false);
assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(null), false);
assert.strictEqual(test_sharedarraybuffer.TestIsSharedArrayBuffer(undefined), false);
}
// Test node_api_create_sharedarraybuffer
{
const sab = test_sharedarraybuffer.TestCreateSharedArrayBuffer(16);
assert(sab instanceof SharedArrayBuffer);
assert.strictEqual(sab.byteLength, 16);
}
// Test node_api_create_get_sharedarraybuffer_info
{
const sab = new SharedArrayBuffer(32);
const byteLength = test_sharedarraybuffer.TestGetSharedArrayBufferInfo(sab);
assert.strictEqual(byteLength, 32);
}
// Test data access
{
const sab = new SharedArrayBuffer(8);
const result = test_sharedarraybuffer.TestSharedArrayBufferData(sab);
assert.strictEqual(result, true);
// Check if data was written correctly
const view = new Uint8Array(sab);
for (let i = 0; i < 8; i++) {
assert.strictEqual(view[i], i % 256);
}
}
// Test data pointer from existing SharedArrayBuffer
{
const sab = new SharedArrayBuffer(16);
const result = test_sharedarraybuffer.TestSharedArrayBufferData(sab);
assert.strictEqual(result, true);
}
// Test zero-length SharedArrayBuffer
{
const sab = test_sharedarraybuffer.TestCreateSharedArrayBuffer(0);
assert(sab instanceof SharedArrayBuffer);
assert.strictEqual(sab.byteLength, 0);
}
// Test invalid arguments
{
assert.throws(() => {
test_sharedarraybuffer.TestGetSharedArrayBufferInfo({});
}, { name: 'Error', message: 'Invalid argument' });
}

View File

@ -0,0 +1,130 @@
#define NAPI_EXPERIMENTAL
#include <js_native_api.h>
#include <string.h>
#include "../common.h"
#include "../entry_point.h"
static napi_value TestIsSharedArrayBuffer(napi_env env,
napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
bool is_sharedarraybuffer;
NODE_API_CALL(
env, node_api_is_sharedarraybuffer(env, args[0], &is_sharedarraybuffer));
napi_value ret;
NODE_API_CALL(env, napi_get_boolean(env, is_sharedarraybuffer, &ret));
return ret;
}
static napi_value TestCreateSharedArrayBuffer(napi_env env,
napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_valuetype valuetype0;
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0));
NODE_API_ASSERT(
env,
valuetype0 == napi_number,
"Wrong type of arguments. Expects a number as first argument.");
int32_t byte_length;
NODE_API_CALL(env, napi_get_value_int32(env, args[0], &byte_length));
NODE_API_ASSERT(env,
byte_length >= 0,
"Invalid byte length. Expects a non-negative integer.");
napi_value ret;
void* data;
NODE_API_CALL(
env, node_api_create_sharedarraybuffer(env, byte_length, &data, &ret));
return ret;
}
static napi_value TestGetSharedArrayBufferInfo(napi_env env,
napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
void* data;
size_t byte_length;
NODE_API_CALL(env,
napi_get_arraybuffer_info(env, args[0], &data, &byte_length));
napi_value ret;
NODE_API_CALL(env, napi_create_uint32(env, byte_length, &ret));
return ret;
}
static void WriteTestDataToBuffer(void* data, size_t byte_length) {
if (byte_length > 0 && data != NULL) {
uint8_t* bytes = (uint8_t*)data;
for (size_t i = 0; i < byte_length; i++) {
bytes[i] = i % 256;
}
}
}
static napi_value TestSharedArrayBufferData(napi_env env,
napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
void* data;
size_t byte_length;
NODE_API_CALL(env,
napi_get_arraybuffer_info(env, args[0], &data, &byte_length));
WriteTestDataToBuffer(data, byte_length);
// Return the same data pointer validity
bool data_valid = (data != NULL) && (byte_length > 0);
napi_value ret;
NODE_API_CALL(env, napi_get_boolean(env, data_valid, &ret));
return ret;
}
EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("TestIsSharedArrayBuffer",
TestIsSharedArrayBuffer),
DECLARE_NODE_API_PROPERTY("TestCreateSharedArrayBuffer",
TestCreateSharedArrayBuffer),
DECLARE_NODE_API_PROPERTY("TestGetSharedArrayBufferInfo",
TestGetSharedArrayBufferInfo),
DECLARE_NODE_API_PROPERTY("TestSharedArrayBufferData",
TestSharedArrayBufferData),
};
NODE_API_CALL(
env,
napi_define_properties(env,
exports,
sizeof(descriptors) / sizeof(*descriptors),
descriptors));
return exports;
}
EXTERN_C_END