Skip to content

Commit 75d9a4e

Browse files
umuoy1aduh95
authored andcommitted
node-api: support SharedArrayBuffer in napi_create_typedarray
Signed-off-by: umuoy1 <burningdian@gmail.com> PR-URL: #62710 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Vladimir Morozov <vmorozov@microsoft.com>
1 parent 9b03b84 commit 75d9a4e

5 files changed

Lines changed: 278 additions & 67 deletions

File tree

doc/api/n-api.md

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,6 +2779,10 @@ Language Specification.
27792779
<!-- YAML
27802780
added: v8.0.0
27812781
napiVersion: 1
2782+
changes:
2783+
- version: REPLACEME
2784+
pr-url: https://github.com/nodejs/node/pull/62710
2785+
description: Added support for `SharedArrayBuffer`.
27822786
-->
27832787

27842788
```c
@@ -2793,21 +2797,25 @@ napi_status napi_create_typedarray(napi_env env,
27932797
* `[in] env`: The environment that the API is invoked under.
27942798
* `[in] type`: Scalar datatype of the elements within the `TypedArray`.
27952799
* `[in] length`: Number of elements in the `TypedArray`.
2796-
* `[in] arraybuffer`: `ArrayBuffer` underlying the typed array.
2797-
* `[in] byte_offset`: The byte offset within the `ArrayBuffer` from which to
2798-
start projecting the `TypedArray`.
2800+
* `[in] arraybuffer`: `ArrayBuffer` or `SharedArrayBuffer` underlying the
2801+
typed array.
2802+
* `[in] byte_offset`: The byte offset within the `ArrayBuffer` or
2803+
`SharedArrayBuffer` from which to start projecting the `TypedArray`.
27992804
* `[out] result`: A `napi_value` representing a JavaScript `TypedArray`.
28002805

28012806
Returns `napi_ok` if the API succeeded.
28022807

28032808
This API creates a JavaScript `TypedArray` object over an existing
2804-
`ArrayBuffer`. `TypedArray` objects provide an array-like view over an
2805-
underlying data buffer where each element has the same underlying binary scalar
2806-
datatype.
2809+
`ArrayBuffer` or `SharedArrayBuffer`. `TypedArray` objects provide an
2810+
array-like view over an underlying data buffer where each element has the same
2811+
underlying binary scalar datatype.
2812+
2813+
It is required that `(length * size_of_element) + byte_offset` is less than or
2814+
equal to the size in bytes of the `ArrayBuffer` or `SharedArrayBuffer` passed
2815+
in. If not, a `RangeError` exception is raised.
28072816

2808-
It's required that `(length * size_of_element) + byte_offset` should
2809-
be <= the size in bytes of the array passed in. If not, a `RangeError` exception
2810-
is raised.
2817+
For element sizes greater than 1, `byte_offset` is required to be a multiple
2818+
of the element size. If not, a `RangeError` exception is raised.
28112819

28122820
JavaScript `TypedArray` objects are described in
28132821
[Section TypedArray objects][] of the ECMAScript Language Specification.
@@ -3496,7 +3504,8 @@ napi_status napi_get_typedarray_info(napi_env env,
34963504
the `byte_offset` value so that it points to the first element in the
34973505
`TypedArray`. If the length of the array is `0`, this may be `NULL` or
34983506
any other pointer value.
3499-
* `[out] arraybuffer`: The `ArrayBuffer` underlying the `TypedArray`.
3507+
* `[out] arraybuffer`: The `ArrayBuffer` or `SharedArrayBuffer` underlying the
3508+
`TypedArray`.
35003509
* `[out] byte_offset`: The byte offset within the underlying native array
35013510
at which the first element of the arrays is located. The value for the data
35023511
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
@@ -3270,66 +3270,73 @@ napi_status NAPI_CDECL napi_create_typedarray(napi_env env,
32703270
CHECK_ARG(env, result);
32713271

32723272
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
3273-
RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
3273+
auto create_typedarray = [&](auto buffer) -> napi_status {
3274+
v8::Local<v8::TypedArray> typedArray;
3275+
3276+
switch (type) {
3277+
case napi_int8_array:
3278+
CREATE_TYPED_ARRAY(
3279+
env, Int8Array, 1, buffer, byte_offset, length, typedArray);
3280+
break;
3281+
case napi_uint8_array:
3282+
CREATE_TYPED_ARRAY(
3283+
env, Uint8Array, 1, buffer, byte_offset, length, typedArray);
3284+
break;
3285+
case napi_uint8_clamped_array:
3286+
CREATE_TYPED_ARRAY(
3287+
env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray);
3288+
break;
3289+
case napi_int16_array:
3290+
CREATE_TYPED_ARRAY(
3291+
env, Int16Array, 2, buffer, byte_offset, length, typedArray);
3292+
break;
3293+
case napi_uint16_array:
3294+
CREATE_TYPED_ARRAY(
3295+
env, Uint16Array, 2, buffer, byte_offset, length, typedArray);
3296+
break;
3297+
case napi_int32_array:
3298+
CREATE_TYPED_ARRAY(
3299+
env, Int32Array, 4, buffer, byte_offset, length, typedArray);
3300+
break;
3301+
case napi_uint32_array:
3302+
CREATE_TYPED_ARRAY(
3303+
env, Uint32Array, 4, buffer, byte_offset, length, typedArray);
3304+
break;
3305+
case napi_float32_array:
3306+
CREATE_TYPED_ARRAY(
3307+
env, Float32Array, 4, buffer, byte_offset, length, typedArray);
3308+
break;
3309+
case napi_float64_array:
3310+
CREATE_TYPED_ARRAY(
3311+
env, Float64Array, 8, buffer, byte_offset, length, typedArray);
3312+
break;
3313+
case napi_bigint64_array:
3314+
CREATE_TYPED_ARRAY(
3315+
env, BigInt64Array, 8, buffer, byte_offset, length, typedArray);
3316+
break;
3317+
case napi_biguint64_array:
3318+
CREATE_TYPED_ARRAY(
3319+
env, BigUint64Array, 8, buffer, byte_offset, length, typedArray);
3320+
break;
3321+
case napi_float16_array:
3322+
CREATE_TYPED_ARRAY(
3323+
env, Float16Array, 2, buffer, byte_offset, length, typedArray);
3324+
break;
3325+
default:
3326+
return napi_set_last_error(env, napi_invalid_arg);
3327+
}
32743328

3275-
v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
3276-
v8::Local<v8::TypedArray> typedArray;
3329+
*result = v8impl::JsValueFromV8LocalValue(typedArray);
3330+
return GET_RETURN_STATUS(env);
3331+
};
32773332

3278-
switch (type) {
3279-
case napi_int8_array:
3280-
CREATE_TYPED_ARRAY(
3281-
env, Int8Array, 1, buffer, byte_offset, length, typedArray);
3282-
break;
3283-
case napi_uint8_array:
3284-
CREATE_TYPED_ARRAY(
3285-
env, Uint8Array, 1, buffer, byte_offset, length, typedArray);
3286-
break;
3287-
case napi_uint8_clamped_array:
3288-
CREATE_TYPED_ARRAY(
3289-
env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray);
3290-
break;
3291-
case napi_int16_array:
3292-
CREATE_TYPED_ARRAY(
3293-
env, Int16Array, 2, buffer, byte_offset, length, typedArray);
3294-
break;
3295-
case napi_uint16_array:
3296-
CREATE_TYPED_ARRAY(
3297-
env, Uint16Array, 2, buffer, byte_offset, length, typedArray);
3298-
break;
3299-
case napi_int32_array:
3300-
CREATE_TYPED_ARRAY(
3301-
env, Int32Array, 4, buffer, byte_offset, length, typedArray);
3302-
break;
3303-
case napi_uint32_array:
3304-
CREATE_TYPED_ARRAY(
3305-
env, Uint32Array, 4, buffer, byte_offset, length, typedArray);
3306-
break;
3307-
case napi_float32_array:
3308-
CREATE_TYPED_ARRAY(
3309-
env, Float32Array, 4, buffer, byte_offset, length, typedArray);
3310-
break;
3311-
case napi_float64_array:
3312-
CREATE_TYPED_ARRAY(
3313-
env, Float64Array, 8, buffer, byte_offset, length, typedArray);
3314-
break;
3315-
case napi_bigint64_array:
3316-
CREATE_TYPED_ARRAY(
3317-
env, BigInt64Array, 8, buffer, byte_offset, length, typedArray);
3318-
break;
3319-
case napi_biguint64_array:
3320-
CREATE_TYPED_ARRAY(
3321-
env, BigUint64Array, 8, buffer, byte_offset, length, typedArray);
3322-
break;
3323-
case napi_float16_array:
3324-
CREATE_TYPED_ARRAY(
3325-
env, Float16Array, 2, buffer, byte_offset, length, typedArray);
3326-
break;
3327-
default:
3328-
return napi_set_last_error(env, napi_invalid_arg);
3333+
if (value->IsArrayBuffer()) {
3334+
return create_typedarray(value.As<v8::ArrayBuffer>());
3335+
} else if (value->IsSharedArrayBuffer()) {
3336+
return create_typedarray(value.As<v8::SharedArrayBuffer>());
3337+
} else {
3338+
return napi_set_last_error(env, napi_invalid_arg);
33293339
}
3330-
3331-
*result = v8impl::JsValueFromV8LocalValue(typedArray);
3332-
return GET_RETURN_STATUS(env);
33333340
}
33343341

33353342
napi_status NAPI_CDECL napi_get_typedarray_info(napi_env env,

test/js-native-api/test_typedarray/binding.gyp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
"sources": [
66
"test_typedarray.c"
77
]
8+
},
9+
{
10+
"target_name": "test_typedarray_sharedarraybuffer",
11+
"sources": [
12+
"test_typedarray_sharedarraybuffer.c"
13+
]
814
}
915
]
1016
}
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+
const typedArrayCases = [
13+
{ type: Int8Array, values: [-1, 0, 127] },
14+
{ type: Uint8Array, values: [1, 2, 255] },
15+
{ type: Uint8ClampedArray, values: [0, 128, 255] },
16+
{ type: Int16Array, values: [-1, 0, 32767] },
17+
{ type: Uint16Array, values: [1, 2, 65535] },
18+
{ type: Int32Array, values: [-1, 0, 123456789] },
19+
{ type: Uint32Array, values: [1, 2, 4294967295] },
20+
{ type: Float16Array, values: [0.5, -1.5, 42.25] },
21+
{ type: Float32Array, values: [0.5, -1.5, 42.25] },
22+
{ type: Float64Array, values: [0.5, -1.5, 42.25] },
23+
{ type: BigInt64Array, values: [1n, -2n, 123456789n] },
24+
{ type: BigUint64Array, values: [1n, 2n, 123456789n] },
25+
];
26+
27+
function createBuffer(Type, BufferType, length) {
28+
const byteOffset = Type.BYTES_PER_ELEMENT;
29+
const byteLength = byteOffset + (length * Type.BYTES_PER_ELEMENT);
30+
return {
31+
buffer: new BufferType(byteLength),
32+
byteOffset,
33+
};
34+
}
35+
36+
function createTypedArray(Type, buffer, byteOffset, length) {
37+
const template = new Type(buffer, byteOffset, length);
38+
return test_typedarray_sharedarraybuffer.CreateTypedArray(template, buffer);
39+
}
40+
41+
function verifyTypedArray(Type, buffer, byteOffset, values) {
42+
const theArray = createTypedArray(Type, buffer, byteOffset, values.length);
43+
const theArrayBuffer =
44+
test_typedarray_sharedarraybuffer.GetArrayBuffer(theArray);
45+
46+
assert.ok(theArray instanceof Type);
47+
assert.strictEqual(theArray.buffer, buffer);
48+
assert.strictEqual(theArrayBuffer, buffer);
49+
assert.strictEqual(theArray.byteOffset, byteOffset);
50+
assert.strictEqual(theArray.length, values.length);
51+
52+
theArray.set(values);
53+
assert.deepStrictEqual(Array.from(new Type(buffer, byteOffset, values.length)),
54+
values);
55+
}
56+
57+
// Keep the existing ArrayBuffer behavior covered while focusing this test
58+
// on SharedArrayBuffer-backed TypedArray creation.
59+
{
60+
const { buffer, byteOffset } = createBuffer(Uint8Array, ArrayBuffer, 3);
61+
verifyTypedArray(Uint8Array, buffer, byteOffset, [1, 2, 3]);
62+
}
63+
64+
// Verify all TypedArray variants can be created from SharedArrayBuffer.
65+
typedArrayCases.forEach(({ type, values }) => {
66+
const { buffer, byteOffset } = createBuffer(type, SharedArrayBuffer,
67+
values.length);
68+
verifyTypedArray(type, buffer, byteOffset, values);
69+
});
70+
71+
// Test for creating TypedArrays with SharedArrayBuffer and invalid range.
72+
for (const { type, values } of typedArrayCases) {
73+
const { buffer, byteOffset } = createBuffer(type, SharedArrayBuffer,
74+
values.length);
75+
const template = new type(buffer, byteOffset, values.length);
76+
77+
assert.throws(() => {
78+
test_typedarray_sharedarraybuffer.CreateTypedArray(
79+
template, buffer, values.length + 1, byteOffset);
80+
}, RangeError);
81+
}
82+
83+
// Test for creating TypedArrays with SharedArrayBuffer and invalid alignment.
84+
for (const { type, values } of typedArrayCases) {
85+
if (type.BYTES_PER_ELEMENT <= 1) {
86+
continue;
87+
}
88+
89+
const { buffer, byteOffset } = createBuffer(type, SharedArrayBuffer,
90+
values.length);
91+
const template = new type(buffer, byteOffset, values.length);
92+
93+
assert.throws(() => {
94+
test_typedarray_sharedarraybuffer.CreateTypedArray(
95+
template, buffer, 1, byteOffset + 1);
96+
}, RangeError);
97+
}
98+
99+
// Test invalid arguments.
100+
{
101+
const template = new Uint8Array(1);
102+
103+
assert.throws(() => {
104+
test_typedarray_sharedarraybuffer.CreateTypedArray(template, {});
105+
}, { name: 'Error', message: 'Invalid argument' });
106+
107+
assert.throws(() => {
108+
test_typedarray_sharedarraybuffer.CreateTypedArray(template, 1);
109+
}, { name: 'Error', message: 'Invalid argument' });
110+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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, napi_callback_info info) {
9+
size_t argc = 4;
10+
napi_value args[4];
11+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
12+
13+
NODE_API_ASSERT(env, argc == 2 || argc == 4, "Wrong number of arguments");
14+
15+
bool is_typedarray;
16+
NODE_API_CALL(env, napi_is_typedarray(env, args[0], &is_typedarray));
17+
NODE_API_ASSERT(env,
18+
is_typedarray,
19+
"Wrong type of arguments. Expects a typed array as first "
20+
"argument.");
21+
22+
napi_typedarray_type type;
23+
size_t length;
24+
size_t byte_offset;
25+
NODE_API_CALL(env,
26+
napi_get_typedarray_info(
27+
env, args[0], &type, &length, NULL, NULL, &byte_offset));
28+
29+
if (argc == 4) {
30+
uint32_t uint32_length;
31+
NODE_API_CALL(env, napi_get_value_uint32(env, args[2], &uint32_length));
32+
length = uint32_length;
33+
34+
uint32_t uint32_byte_offset;
35+
NODE_API_CALL(env,
36+
napi_get_value_uint32(env, args[3], &uint32_byte_offset));
37+
byte_offset = uint32_byte_offset;
38+
}
39+
40+
napi_value typedarray;
41+
NODE_API_CALL(env,
42+
napi_create_typedarray(
43+
env, type, length, args[1], byte_offset, &typedarray));
44+
45+
return typedarray;
46+
}
47+
48+
static napi_value GetArrayBuffer(napi_env env, napi_callback_info info) {
49+
size_t argc = 1;
50+
napi_value args[1];
51+
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
52+
53+
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
54+
55+
napi_value arraybuffer;
56+
NODE_API_CALL(env,
57+
napi_get_typedarray_info(
58+
env, args[0], NULL, NULL, NULL, &arraybuffer, NULL));
59+
60+
return arraybuffer;
61+
}
62+
63+
EXTERN_C_START
64+
napi_value Init(napi_env env, napi_value exports) {
65+
napi_property_descriptor descriptors[] = {
66+
DECLARE_NODE_API_PROPERTY("CreateTypedArray", CreateTypedArray),
67+
DECLARE_NODE_API_PROPERTY("GetArrayBuffer", GetArrayBuffer),
68+
};
69+
70+
NODE_API_CALL(
71+
env,
72+
napi_define_properties(env,
73+
exports,
74+
sizeof(descriptors) / sizeof(*descriptors),
75+
descriptors));
76+
77+
return exports;
78+
}
79+
EXTERN_C_END

0 commit comments

Comments
 (0)