Skip to content

Commit 983f644

Browse files
committed
node-api: support SharedArrayBuffer in napi_create_typedarray
Signed-off-by: umuoy1 <burningdian@gmail.com>
1 parent dfe438d commit 983f644

File tree

5 files changed

+272
-67
lines changed

5 files changed

+272
-67
lines changed

doc/api/n-api.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,21 +2762,22 @@ napi_status napi_create_typedarray(napi_env env,
27622762
* `[in] env`: The environment that the API is invoked under.
27632763
* `[in] type`: Scalar datatype of the elements within the `TypedArray`.
27642764
* `[in] length`: Number of elements in the `TypedArray`.
2765-
* `[in] arraybuffer`: `ArrayBuffer` underlying the typed array.
2766-
* `[in] byte_offset`: The byte offset within the `ArrayBuffer` from which to
2767-
start projecting the `TypedArray`.
2765+
* `[in] arraybuffer`: `ArrayBuffer` or `SharedArrayBuffer` underlying the
2766+
typed array.
2767+
* `[in] byte_offset`: The byte offset within the `ArrayBuffer` or
2768+
`SharedArrayBuffer` from which to start projecting the `TypedArray`.
27682769
* `[out] result`: A `napi_value` representing a JavaScript `TypedArray`.
27692770

27702771
Returns `napi_ok` if the API succeeded.
27712772

27722773
This API creates a JavaScript `TypedArray` object over an existing
2773-
`ArrayBuffer`. `TypedArray` objects provide an array-like view over an
2774-
underlying data buffer where each element has the same underlying binary scalar
2775-
datatype.
2774+
`ArrayBuffer` or `SharedArrayBuffer`. `TypedArray` objects provide an
2775+
array-like view over an underlying data buffer where each element has the same
2776+
underlying binary scalar datatype.
27762777

2777-
It's required that `(length * size_of_element) + byte_offset` should
2778-
be <= the size in bytes of the array passed in. If not, a `RangeError` exception
2779-
is raised.
2778+
It is required that `(length * size_of_element) + byte_offset` is less than or
2779+
equal to the size in bytes of the `ArrayBuffer` or `SharedArrayBuffer` passed
2780+
in. If not, a `RangeError` exception is raised.
27802781

27812782
JavaScript `TypedArray` objects are described in
27822783
[Section TypedArray objects][] of the ECMAScript Language Specification.
@@ -3469,7 +3470,8 @@ napi_status napi_get_typedarray_info(napi_env env,
34693470
the `byte_offset` value so that it points to the first element in the
34703471
`TypedArray`. If the length of the array is `0`, this may be `NULL` or
34713472
any other pointer value.
3472-
* `[out] arraybuffer`: The `ArrayBuffer` underlying the `TypedArray`.
3473+
* `[out] arraybuffer`: The `ArrayBuffer` or `SharedArrayBuffer` underlying the
3474+
`TypedArray`.
34733475
* `[out] byte_offset`: The byte offset within the underlying native array
34743476
at which the first element of the arrays is located. The value for the data
34753477
parameter has already been adjusted so that data points to the first element

src/js_native_api_v8.cc

Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3228,66 +3228,73 @@ napi_status NAPI_CDECL napi_create_typedarray(napi_env env,
32283228
CHECK_ARG(env, result);
32293229

32303230
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
3231-
RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
3231+
auto create_typedarray = [&](auto buffer) -> napi_status {
3232+
v8::Local<v8::TypedArray> typedArray;
3233+
3234+
switch (type) {
3235+
case napi_int8_array:
3236+
CREATE_TYPED_ARRAY(
3237+
env, Int8Array, 1, buffer, byte_offset, length, typedArray);
3238+
break;
3239+
case napi_uint8_array:
3240+
CREATE_TYPED_ARRAY(
3241+
env, Uint8Array, 1, buffer, byte_offset, length, typedArray);
3242+
break;
3243+
case napi_uint8_clamped_array:
3244+
CREATE_TYPED_ARRAY(
3245+
env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray);
3246+
break;
3247+
case napi_int16_array:
3248+
CREATE_TYPED_ARRAY(
3249+
env, Int16Array, 2, buffer, byte_offset, length, typedArray);
3250+
break;
3251+
case napi_uint16_array:
3252+
CREATE_TYPED_ARRAY(
3253+
env, Uint16Array, 2, buffer, byte_offset, length, typedArray);
3254+
break;
3255+
case napi_int32_array:
3256+
CREATE_TYPED_ARRAY(
3257+
env, Int32Array, 4, buffer, byte_offset, length, typedArray);
3258+
break;
3259+
case napi_uint32_array:
3260+
CREATE_TYPED_ARRAY(
3261+
env, Uint32Array, 4, buffer, byte_offset, length, typedArray);
3262+
break;
3263+
case napi_float32_array:
3264+
CREATE_TYPED_ARRAY(
3265+
env, Float32Array, 4, buffer, byte_offset, length, typedArray);
3266+
break;
3267+
case napi_float64_array:
3268+
CREATE_TYPED_ARRAY(
3269+
env, Float64Array, 8, buffer, byte_offset, length, typedArray);
3270+
break;
3271+
case napi_bigint64_array:
3272+
CREATE_TYPED_ARRAY(
3273+
env, BigInt64Array, 8, buffer, byte_offset, length, typedArray);
3274+
break;
3275+
case napi_biguint64_array:
3276+
CREATE_TYPED_ARRAY(
3277+
env, BigUint64Array, 8, buffer, byte_offset, length, typedArray);
3278+
break;
3279+
case napi_float16_array:
3280+
CREATE_TYPED_ARRAY(
3281+
env, Float16Array, 2, buffer, byte_offset, length, typedArray);
3282+
break;
3283+
default:
3284+
return napi_set_last_error(env, napi_invalid_arg);
3285+
}
32323286

3233-
v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
3234-
v8::Local<v8::TypedArray> typedArray;
3287+
*result = v8impl::JsValueFromV8LocalValue(typedArray);
3288+
return GET_RETURN_STATUS(env);
3289+
};
32353290

3236-
switch (type) {
3237-
case napi_int8_array:
3238-
CREATE_TYPED_ARRAY(
3239-
env, Int8Array, 1, buffer, byte_offset, length, typedArray);
3240-
break;
3241-
case napi_uint8_array:
3242-
CREATE_TYPED_ARRAY(
3243-
env, Uint8Array, 1, buffer, byte_offset, length, typedArray);
3244-
break;
3245-
case napi_uint8_clamped_array:
3246-
CREATE_TYPED_ARRAY(
3247-
env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray);
3248-
break;
3249-
case napi_int16_array:
3250-
CREATE_TYPED_ARRAY(
3251-
env, Int16Array, 2, buffer, byte_offset, length, typedArray);
3252-
break;
3253-
case napi_uint16_array:
3254-
CREATE_TYPED_ARRAY(
3255-
env, Uint16Array, 2, buffer, byte_offset, length, typedArray);
3256-
break;
3257-
case napi_int32_array:
3258-
CREATE_TYPED_ARRAY(
3259-
env, Int32Array, 4, buffer, byte_offset, length, typedArray);
3260-
break;
3261-
case napi_uint32_array:
3262-
CREATE_TYPED_ARRAY(
3263-
env, Uint32Array, 4, buffer, byte_offset, length, typedArray);
3264-
break;
3265-
case napi_float32_array:
3266-
CREATE_TYPED_ARRAY(
3267-
env, Float32Array, 4, buffer, byte_offset, length, typedArray);
3268-
break;
3269-
case napi_float64_array:
3270-
CREATE_TYPED_ARRAY(
3271-
env, Float64Array, 8, buffer, byte_offset, length, typedArray);
3272-
break;
3273-
case napi_bigint64_array:
3274-
CREATE_TYPED_ARRAY(
3275-
env, BigInt64Array, 8, buffer, byte_offset, length, typedArray);
3276-
break;
3277-
case napi_biguint64_array:
3278-
CREATE_TYPED_ARRAY(
3279-
env, BigUint64Array, 8, buffer, byte_offset, length, typedArray);
3280-
break;
3281-
case napi_float16_array:
3282-
CREATE_TYPED_ARRAY(
3283-
env, Float16Array, 2, buffer, byte_offset, length, typedArray);
3284-
break;
3285-
default:
3286-
return napi_set_last_error(env, napi_invalid_arg);
3291+
if (value->IsArrayBuffer()) {
3292+
return create_typedarray(value.As<v8::ArrayBuffer>());
3293+
} else if (value->IsSharedArrayBuffer()) {
3294+
return create_typedarray(value.As<v8::SharedArrayBuffer>());
3295+
} else {
3296+
return napi_set_last_error(env, napi_invalid_arg);
32873297
}
3288-
3289-
*result = v8impl::JsValueFromV8LocalValue(typedArray);
3290-
return GET_RETURN_STATUS(env);
32913298
}
32923299

32933300
napi_status NAPI_CDECL napi_get_typedarray_info(napi_env env,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "test_typedarray_sharedarraybuffer",
5+
"sources": [
6+
"test_typedarray_sharedarraybuffer.c"
7+
]
8+
}
9+
]
10+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
'use strict';
2+
3+
// Verify SharedArrayBuffer-backed typed arrays can be created through
4+
// napi_create_typedarray() while preserving existing ArrayBuffer behavior.
5+
6+
const common = require('../../common');
7+
const assert = require('assert');
8+
9+
const test_typedarray_sharedarraybuffer =
10+
require(`./build/${common.buildType}/test_typedarray_sharedarraybuffer`);
11+
12+
// Test for creating Uint8Array with ArrayBuffer
13+
{
14+
const buffer = new ArrayBuffer(16);
15+
const theArray =
16+
test_typedarray_sharedarraybuffer.CreateUint8Array(buffer, 4, 6);
17+
const theArrayBuffer =
18+
test_typedarray_sharedarraybuffer.GetArrayBuffer(theArray);
19+
20+
assert.ok(theArray instanceof Uint8Array);
21+
assert.strictEqual(theArray.buffer, buffer);
22+
assert.ok(theArrayBuffer instanceof ArrayBuffer);
23+
assert.strictEqual(theArrayBuffer, buffer);
24+
assert.strictEqual(theArray.byteOffset, 4);
25+
assert.strictEqual(theArray.length, 6);
26+
27+
theArray.set([1, 2, 3, 4, 5, 6]);
28+
assert.deepStrictEqual(Array.from(new Uint8Array(buffer, 4, 6)),
29+
[1, 2, 3, 4, 5, 6]);
30+
}
31+
32+
// Test for creating Uint8Array with SharedArrayBuffer
33+
{
34+
const buffer = new SharedArrayBuffer(16);
35+
const theArray =
36+
test_typedarray_sharedarraybuffer.CreateUint8Array(buffer, 4, 6);
37+
const theArrayBuffer =
38+
test_typedarray_sharedarraybuffer.GetArrayBuffer(theArray);
39+
40+
assert.ok(theArray instanceof Uint8Array);
41+
assert.strictEqual(theArray.buffer, buffer);
42+
assert.ok(theArrayBuffer instanceof SharedArrayBuffer);
43+
assert.strictEqual(theArrayBuffer, buffer);
44+
assert.strictEqual(theArray.byteOffset, 4);
45+
assert.strictEqual(theArray.length, 6);
46+
47+
theArray.set([6, 5, 4, 3, 2, 1]);
48+
assert.deepStrictEqual(Array.from(new Uint8Array(buffer, 4, 6)),
49+
[6, 5, 4, 3, 2, 1]);
50+
}
51+
52+
// Test for creating Uint16Array with SharedArrayBuffer
53+
{
54+
const buffer = new SharedArrayBuffer(24);
55+
const theArray =
56+
test_typedarray_sharedarraybuffer.CreateUint16Array(buffer, 4, 4);
57+
58+
assert.ok(theArray instanceof Uint16Array);
59+
assert.strictEqual(theArray.buffer, buffer);
60+
assert.strictEqual(theArray.byteOffset, 4);
61+
assert.strictEqual(theArray.length, 4);
62+
63+
theArray.set([1, 2, 3, 65535]);
64+
assert.deepStrictEqual(Array.from(new Uint16Array(buffer, 4, 4)),
65+
[1, 2, 3, 65535]);
66+
}
67+
68+
// Test for creating Int32Array with SharedArrayBuffer
69+
{
70+
const buffer = new SharedArrayBuffer(32);
71+
const theArray =
72+
test_typedarray_sharedarraybuffer.CreateInt32Array(buffer, 8, 3);
73+
74+
assert.ok(theArray instanceof Int32Array);
75+
assert.strictEqual(theArray.buffer, buffer);
76+
assert.strictEqual(theArray.byteOffset, 8);
77+
assert.strictEqual(theArray.length, 3);
78+
79+
theArray.set([-1, 0, 123456789]);
80+
assert.deepStrictEqual(Array.from(new Int32Array(buffer, 8, 3)),
81+
[-1, 0, 123456789]);
82+
}
83+
84+
// Test for creating TypedArrays with SharedArrayBuffer and invalid range
85+
{
86+
const buffer = new SharedArrayBuffer(8);
87+
88+
assert.throws(() => {
89+
test_typedarray_sharedarraybuffer.CreateUint8Array(buffer, 9, 0);
90+
}, RangeError);
91+
92+
assert.throws(() => {
93+
test_typedarray_sharedarraybuffer.CreateUint16Array(buffer, 0, 5);
94+
}, RangeError);
95+
96+
assert.throws(() => {
97+
test_typedarray_sharedarraybuffer.CreateUint16Array(buffer, 1, 1);
98+
}, RangeError);
99+
}
100+
101+
// Test invalid arguments
102+
{
103+
assert.throws(() => {
104+
test_typedarray_sharedarraybuffer.CreateUint8Array({}, 0, 1);
105+
}, { name: 'Error', message: 'Invalid argument' });
106+
107+
assert.throws(() => {
108+
test_typedarray_sharedarraybuffer.CreateUint8Array(1, 0, 1);
109+
}, { name: 'Error', message: 'Invalid argument' });
110+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Verify napi_create_typedarray() accepts SharedArrayBuffer-backed views
2+
// without changing its existing error handling.
3+
4+
#include <js_native_api.h>
5+
#include "../common.h"
6+
#include "../entry_point.h"
7+
8+
static napi_value CreateTypedArray(napi_env env,
9+
napi_callback_info info,
10+
napi_typedarray_type type) {
11+
size_t argc = 3;
12+
napi_value args[3];
13+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
14+
15+
NODE_API_ASSERT(env, argc == 3, "Wrong number of arguments");
16+
17+
uint32_t byte_offset;
18+
NODE_API_CALL(env, napi_get_value_uint32(env, args[1], &byte_offset));
19+
20+
uint32_t length;
21+
NODE_API_CALL(env, napi_get_value_uint32(env, args[2], &length));
22+
23+
napi_value typedarray;
24+
NODE_API_CALL(env,
25+
napi_create_typedarray(env,
26+
type,
27+
length,
28+
args[0],
29+
byte_offset,
30+
&typedarray));
31+
32+
return typedarray;
33+
}
34+
35+
static napi_value CreateUint8Array(napi_env env, napi_callback_info info) {
36+
return CreateTypedArray(env, info, napi_uint8_array);
37+
}
38+
39+
static napi_value CreateUint16Array(napi_env env, napi_callback_info info) {
40+
return CreateTypedArray(env, info, napi_uint16_array);
41+
}
42+
43+
static napi_value CreateInt32Array(napi_env env, napi_callback_info info) {
44+
return CreateTypedArray(env, info, napi_int32_array);
45+
}
46+
47+
static napi_value GetArrayBuffer(napi_env env, napi_callback_info info) {
48+
size_t argc = 1;
49+
napi_value args[1];
50+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
51+
52+
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
53+
54+
napi_value arraybuffer;
55+
NODE_API_CALL(
56+
env,
57+
napi_get_typedarray_info(env, args[0], NULL, NULL, NULL, &arraybuffer, NULL));
58+
59+
return arraybuffer;
60+
}
61+
62+
EXTERN_C_START
63+
napi_value Init(napi_env env, napi_value exports) {
64+
napi_property_descriptor descriptors[] = {
65+
DECLARE_NODE_API_PROPERTY("CreateUint8Array", CreateUint8Array),
66+
DECLARE_NODE_API_PROPERTY("CreateUint16Array", CreateUint16Array),
67+
DECLARE_NODE_API_PROPERTY("CreateInt32Array", CreateInt32Array),
68+
DECLARE_NODE_API_PROPERTY("GetArrayBuffer", GetArrayBuffer),
69+
};
70+
71+
NODE_API_CALL(env, napi_define_properties(
72+
env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));
73+
74+
return exports;
75+
}
76+
EXTERN_C_END

0 commit comments

Comments
 (0)