node-api: support SharedArrayBuffer in napi_create_typedarray · nodejs/node@3909ff2 · GitHub
Skip to content

Commit 3909ff2

Browse files
umuoy1juanarbol
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> Signed-off-by: Juan José Arboleda <soyjuanarbol@gmail.com>
1 parent fdc65e4 commit 3909ff2

5 files changed

Lines changed: 273 additions & 63 deletions

File tree

‎doc/api/n-api.md‎

Lines changed: 19 additions & 10 deletions

‎src/js_native_api_v8.cc‎

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3204,62 +3204,69 @@ napi_status NAPI_CDECL napi_create_typedarray(napi_env env,
32043204
CHECK_ARG(env, result);
32053205

32063206
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
3207-
RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
3207+
auto create_typedarray = [&](auto buffer) -> napi_status {
3208+
v8::Local<v8::TypedArray> typedArray;
3209+
3210+
switch (type) {
3211+
case napi_int8_array:
3212+
CREATE_TYPED_ARRAY(
3213+
env, Int8Array, 1, buffer, byte_offset, length, typedArray);
3214+
break;
3215+
case napi_uint8_array:
3216+
CREATE_TYPED_ARRAY(
3217+
env, Uint8Array, 1, buffer, byte_offset, length, typedArray);
3218+
break;
3219+
case napi_uint8_clamped_array:
3220+
CREATE_TYPED_ARRAY(
3221+
env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray);
3222+
break;
3223+
case napi_int16_array:
3224+
CREATE_TYPED_ARRAY(
3225+
env, Int16Array, 2, buffer, byte_offset, length, typedArray);
3226+
break;
3227+
case napi_uint16_array:
3228+
CREATE_TYPED_ARRAY(
3229+
env, Uint16Array, 2, buffer, byte_offset, length, typedArray);
3230+
break;
3231+
case napi_int32_array:
3232+
CREATE_TYPED_ARRAY(
3233+
env, Int32Array, 4, buffer, byte_offset, length, typedArray);
3234+
break;
3235+
case napi_uint32_array:
3236+
CREATE_TYPED_ARRAY(
3237+
env, Uint32Array, 4, buffer, byte_offset, length, typedArray);
3238+
break;
3239+
case napi_float32_array:
3240+
CREATE_TYPED_ARRAY(
3241+
env, Float32Array, 4, buffer, byte_offset, length, typedArray);
3242+
break;
3243+
case napi_float64_array:
3244+
CREATE_TYPED_ARRAY(
3245+
env, Float64Array, 8, buffer, byte_offset, length, typedArray);
3246+
break;
3247+
case napi_bigint64_array:
3248+
CREATE_TYPED_ARRAY(
3249+
env, BigInt64Array, 8, buffer, byte_offset, length, typedArray);
3250+
break;
3251+
case napi_biguint64_array:
3252+
CREATE_TYPED_ARRAY(
3253+
env, BigUint64Array, 8, buffer, byte_offset, length, typedArray);
3254+
break;
3255+
default:
3256+
return napi_set_last_error(env, napi_invalid_arg);
3257+
}
32083258

3209-
v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
3210-
v8::Local<v8::TypedArray> typedArray;
3259+
*result = v8impl::JsValueFromV8LocalValue(typedArray);
3260+
return GET_RETURN_STATUS(env);
3261+
};
32113262

3212-
switch (type) {
3213-
case napi_int8_array:
3214-
CREATE_TYPED_ARRAY(
3215-
env, Int8Array, 1, buffer, byte_offset, length, typedArray);
3216-
break;
3217-
case napi_uint8_array:
3218-
CREATE_TYPED_ARRAY(
3219-
env, Uint8Array, 1, buffer, byte_offset, length, typedArray);
3220-
break;
3221-
case napi_uint8_clamped_array:
3222-
CREATE_TYPED_ARRAY(
3223-
env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray);
3224-
break;
3225-
case napi_int16_array:
3226-
CREATE_TYPED_ARRAY(
3227-
env, Int16Array, 2, buffer, byte_offset, length, typedArray);
3228-
break;
3229-
case napi_uint16_array:
3230-
CREATE_TYPED_ARRAY(
3231-
env, Uint16Array, 2, buffer, byte_offset, length, typedArray);
3232-
break;
3233-
case napi_int32_array:
3234-
CREATE_TYPED_ARRAY(
3235-
env, Int32Array, 4, buffer, byte_offset, length, typedArray);
3236-
break;
3237-
case napi_uint32_array:
3238-
CREATE_TYPED_ARRAY(
3239-
env, Uint32Array, 4, buffer, byte_offset, length, typedArray);
3240-
break;
3241-
case napi_float32_array:
3242-
CREATE_TYPED_ARRAY(
3243-
env, Float32Array, 4, buffer, byte_offset, length, typedArray);
3244-
break;
3245-
case napi_float64_array:
3246-
CREATE_TYPED_ARRAY(
3247-
env, Float64Array, 8, buffer, byte_offset, length, typedArray);
3248-
break;
3249-
case napi_bigint64_array:
3250-
CREATE_TYPED_ARRAY(
3251-
env, BigInt64Array, 8, buffer, byte_offset, length, typedArray);
3252-
break;
3253-
case napi_biguint64_array:
3254-
CREATE_TYPED_ARRAY(
3255-
env, BigUint64Array, 8, buffer, byte_offset, length, typedArray);
3256-
break;
3257-
default:
3258-
return napi_set_last_error(env, napi_invalid_arg);
3263+
if (value->IsArrayBuffer()) {
3264+
return create_typedarray(value.As<v8::ArrayBuffer>());
3265+
} else if (value->IsSharedArrayBuffer()) {
3266+
return create_typedarray(value.As<v8::SharedArrayBuffer>());
3267+
} else {
3268+
return napi_set_last_error(env, napi_invalid_arg);
32593269
}
3260-
3261-
*result = v8impl::JsValueFromV8LocalValue(typedArray);
3262-
return GET_RETURN_STATUS(env);
32633270
}
32643271

32653272
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: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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: Float32Array, values: [0.5, -1.5, 42.25] },
21+
{ type: Float64Array, values: [0.5, -1.5, 42.25] },
22+
{ type: BigInt64Array, values: [1n, -2n, 123456789n] },
23+
{ type: BigUint64Array, values: [1n, 2n, 123456789n] },
24+
];
25+
26+
function createBuffer(Type, BufferType, length) {
27+
const byteOffset = Type.BYTES_PER_ELEMENT;
28+
const byteLength = byteOffset + (length * Type.BYTES_PER_ELEMENT);
29+
return {
30+
buffer: new BufferType(byteLength),
31+
byteOffset,
32+
};
33+
}
34+
35+
function createTypedArray(Type, buffer, byteOffset, length) {
36+
const template = new Type(buffer, byteOffset, length);
37+
return test_typedarray_sharedarraybuffer.CreateTypedArray(template, buffer);
38+
}
39+
40+
function verifyTypedArray(Type, buffer, byteOffset, values) {
41+
const theArray = createTypedArray(Type, buffer, byteOffset, values.length);
42+
const theArrayBuffer =
43+
test_typedarray_sharedarraybuffer.GetArrayBuffer(theArray);
44+
45+
assert.ok(theArray instanceof Type);
46+
assert.strictEqual(theArray.buffer, buffer);
47+
assert.strictEqual(theArrayBuffer, buffer);
48+
assert.strictEqual(theArray.byteOffset, byteOffset);
49+
assert.strictEqual(theArray.length, values.length);
50+
51+
theArray.set(values);
52+
assert.deepStrictEqual(Array.from(new Type(buffer, byteOffset, values.length)),
53+
values);
54+
}
55+
56+
// Keep the existing ArrayBuffer behavior covered while focusing this test
57+
// on SharedArrayBuffer-backed TypedArray creation.
58+
{
59+
const { buffer, byteOffset } = createBuffer(Uint8Array, ArrayBuffer, 3);
60+
verifyTypedArray(Uint8Array, buffer, byteOffset, [1, 2, 3]);
61+
}
62+
63+
// Verify all TypedArray variants can be created from SharedArrayBuffer.
64+
typedArrayCases.forEach(({ type, values }) => {
65+
const { buffer, byteOffset } = createBuffer(type, SharedArrayBuffer,
66+
values.length);
67+
verifyTypedArray(type, buffer, byteOffset, values);
68+
});
69+
70+
// Test for creating TypedArrays with SharedArrayBuffer and invalid range.
71+
for (const { type, values } of typedArrayCases) {
72+
const { buffer, byteOffset } = createBuffer(type, SharedArrayBuffer,
73+
values.length);
74+
const template = new type(buffer, byteOffset, values.length);
75+
76+
assert.throws(() => {
77+
test_typedarray_sharedarraybuffer.CreateTypedArray(
78+
template, buffer, values.length + 1, byteOffset);
79+
}, RangeError);
80+
}
81+
82+
// Test for creating TypedArrays with SharedArrayBuffer and invalid alignment.
83+
for (const { type, values } of typedArrayCases) {
84+
if (type.BYTES_PER_ELEMENT <= 1) {
85+
continue;
86+
}
87+
88+
const { buffer, byteOffset } = createBuffer(type, SharedArrayBuffer,
89+
values.length);
90+
const template = new type(buffer, byteOffset, values.length);
91+
92+
assert.throws(() => {
93+
test_typedarray_sharedarraybuffer.CreateTypedArray(
94+
template, buffer, 1, byteOffset + 1);
95+
}, RangeError);
96+
}
97+
98+
// Test invalid arguments.
99+
{
100+
const template = new Uint8Array(1);
101+
102+
assert.throws(() => {
103+
test_typedarray_sharedarraybuffer.CreateTypedArray(template, {});
104+
}, { name: 'Error', message: 'Invalid argument' });
105+
106+
assert.throws(() => {
107+
test_typedarray_sharedarraybuffer.CreateTypedArray(template, 1);
108+
}, { name: 'Error', message: 'Invalid argument' });
109+
}
Lines changed: 79 additions & 0 deletions

0 commit comments

Comments
 (0)