Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions implementors/node/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,13 @@ globalThis.runtimeFeatures = {
major > 25 ||
(major === 25 && minor >= 4) ||
(major === 24 && (minor > 13 || (minor === 13 && patch >= 1))),

// napi_create_typedarray accepts a SharedArrayBuffer-backed buffer only
// since Node.js v26.2.0 and v24.18.0 (nodejs/node#62710). The v25.x line
// saw no release after that landed, and it was not backported to v20.x or
// v22.x, where such calls fail with "invalid argument".
typedarraySharedArrayBuffer:
major > 26 ||
(major === 26 && minor >= 2) ||
(major === 24 && minor >= 18),
};
1 change: 1 addition & 0 deletions tests/js-native-api/test_typedarray/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_node_api_cts_addon(test_typedarray test_typedarray.c)
add_node_api_cts_addon(test_typedarray_sharedarraybuffer test_typedarray_sharedarraybuffer.c)
117 changes: 117 additions & 0 deletions tests/js-native-api/test_typedarray/test_sharedarraybuffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// napi_create_typedarray accepts a SharedArrayBuffer-backed buffer only on
// newer Node.js releases (see implementors/node/features.js).
if (!runtimeFeatures.typedarraySharedArrayBuffer) {
skipTest();
}

// Verify SharedArrayBuffer-backed typed arrays can be created through
// napi_create_typedarray, while preserving the existing ArrayBuffer behavior.
const addon = loadAddon('test_typedarray_sharedarraybuffer');

// Float16Array is an ES2025 addition, not exposed by default in older V8
// versions. See the longer note in test.js.
const hasFloat16Array = typeof Float16Array !== 'undefined';

const typedArrayCases = [
{ type: Int8Array, values: [-1, 0, 127] },
{ type: Uint8Array, values: [1, 2, 255] },
{ type: Uint8ClampedArray, values: [0, 128, 255] },
{ type: Int16Array, values: [-1, 0, 32767] },
{ type: Uint16Array, values: [1, 2, 65535] },
{ type: Int32Array, values: [-1, 0, 123456789] },
{ type: Uint32Array, values: [1, 2, 4294967295] },
...(hasFloat16Array ? [{ type: Float16Array, values: [0.5, -1.5, 42.25] }] : []),
{ type: Float32Array, values: [0.5, -1.5, 42.25] },
{ type: Float64Array, values: [0.5, -1.5, 42.25] },
{ type: BigInt64Array, values: [1n, -2n, 123456789n] },
{ type: BigUint64Array, values: [1n, 2n, 123456789n] },
];

function createBuffer(Type, BufferType, length) {
const byteOffset = Type.BYTES_PER_ELEMENT;
const byteLength = byteOffset + (length * Type.BYTES_PER_ELEMENT);
return {
buffer: new BufferType(byteLength),
byteOffset,
};
}

function verifyTypedArray(Type, buffer, byteOffset, values) {
const template = new Type(buffer, byteOffset, values.length);
const theArray = addon.CreateTypedArray(template, buffer);
const theArrayBuffer = addon.GetArrayBuffer(theArray);

assert.ok(theArray instanceof Type);
assert.strictEqual(theArray.buffer, buffer);
assert.strictEqual(theArrayBuffer, buffer);
assert.strictEqual(theArray.byteOffset, byteOffset);
assert.strictEqual(theArray.length, values.length);

theArray.set(values);
assert.deepStrictEqual(
Array.from(new Type(buffer, byteOffset, values.length)),
values,
);
}

// Keep the existing ArrayBuffer behavior covered while focusing this test
// on SharedArrayBuffer-backed TypedArray creation.
{
const { buffer, byteOffset } = createBuffer(Uint8Array, ArrayBuffer, 3);
verifyTypedArray(Uint8Array, buffer, byteOffset, [1, 2, 3]);
}

// Verify all TypedArray variants can be created from a SharedArrayBuffer.
for (const { type, values } of typedArrayCases) {
const { buffer, byteOffset } = createBuffer(
type,
SharedArrayBuffer,
values.length,
);
verifyTypedArray(type, buffer, byteOffset, values);
}

// Test for creating TypedArrays with SharedArrayBuffer and invalid range.
for (const { type, values } of typedArrayCases) {
const { buffer, byteOffset } = createBuffer(
type,
SharedArrayBuffer,
values.length,
);
const template = new type(buffer, byteOffset, values.length);

assert.throws(() => {
addon.CreateTypedArray(template, buffer, values.length + 1, byteOffset);
}, RangeError);
}

// Test for creating TypedArrays with SharedArrayBuffer and invalid alignment.
for (const { type, values } of typedArrayCases) {
if (type.BYTES_PER_ELEMENT <= 1) {
continue;
}

const { buffer, byteOffset } = createBuffer(
type,
SharedArrayBuffer,
values.length,
);
const template = new type(buffer, byteOffset, values.length);

assert.throws(() => {
addon.CreateTypedArray(template, buffer, 1, byteOffset + 1);
}, RangeError);
}

// Test invalid arguments.
{
const template = new Uint8Array(1);

assert.throws(() => {
addon.CreateTypedArray(template, {});
}, { name: 'Error', message: 'Invalid argument' });

assert.throws(() => {
addon.CreateTypedArray(template, 1);
}, { name: 'Error', message: 'Invalid argument' });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Verify napi_create_typedarray() accepts SharedArrayBuffer-backed views
// without changing its existing error handling.

#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"

static napi_value CreateTypedArray(napi_env env, napi_callback_info info) {
size_t argc = 4;
napi_value args[4];
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));

NODE_API_ASSERT(env, argc == 2 || argc == 4, "Wrong number of arguments");

bool is_typedarray;
NODE_API_CALL(env, napi_is_typedarray(env, args[0], &is_typedarray));
NODE_API_ASSERT(env,
is_typedarray,
"Wrong type of arguments. Expects a typed array as first "
"argument.");

napi_typedarray_type type;
size_t length;
size_t byte_offset;
NODE_API_CALL(env,
napi_get_typedarray_info(
env, args[0], &type, &length, NULL, NULL, &byte_offset));

if (argc == 4) {
uint32_t uint32_length;
NODE_API_CALL(env, napi_get_value_uint32(env, args[2], &uint32_length));
length = uint32_length;

uint32_t uint32_byte_offset;
NODE_API_CALL(env,
napi_get_value_uint32(env, args[3], &uint32_byte_offset));
byte_offset = uint32_byte_offset;
}

napi_value typedarray;
NODE_API_CALL(env,
napi_create_typedarray(
env, type, length, args[1], byte_offset, &typedarray));

return typedarray;
}

static napi_value GetArrayBuffer(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_value arraybuffer;
NODE_API_CALL(env,
napi_get_typedarray_info(
env, args[0], NULL, NULL, NULL, &arraybuffer, NULL));

return arraybuffer;
}

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("CreateTypedArray", CreateTypedArray),
DECLARE_NODE_API_PROPERTY("GetArrayBuffer", GetArrayBuffer),
};

NODE_API_CALL(
env,
napi_define_properties(env,
exports,
sizeof(descriptors) / sizeof(*descriptors),
descriptors));

return exports;
}
EXTERN_C_END
Loading