From 59ab50e1b39c8750709e5c4b8ff40dbd764969f1 Mon Sep 17 00:00:00 2001 From: Balakrishna Avulapati Date: Wed, 1 Apr 2026 11:31:26 +0530 Subject: [PATCH 1/2] port test_sharedarraybuffer to CTS ports [test_sharedarraybuffer] from the Node.js test suite to the CTS. Signed-off-by: Balakrishna Avulapati --- CMakeLists.txt | 3 +- PORTING.md | 2 +- .../test_sharedarraybuffer/CMakeLists.txt | 1 + .../test_sharedarraybuffer/test.js | 88 ++++++++++++ .../test_sharedarraybuffer.c | 130 ++++++++++++++++++ 5 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 tests/js-native-api/test_sharedarraybuffer/CMakeLists.txt create mode 100644 tests/js-native-api/test_sharedarraybuffer/test.js create mode 100644 tests/js-native-api/test_sharedarraybuffer/test_sharedarraybuffer.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 32578ac..cf67540 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,8 +45,7 @@ function(add_node_api_cts_addon ADDON_NAME) endfunction() function(add_node_api_cts_experimental_addon ADDON_NAME) - cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "SOURCES") - add_node_api_cts_addon(${ADDON_NAME} ${ARG_SOURCES}) + add_node_api_cts_addon(${ADDON_NAME} ${ARGN}) target_compile_definitions(${ADDON_NAME} PRIVATE NAPI_EXPERIMENTAL) if(MSVC) target_link_libraries(${ADDON_NAME} PRIVATE ${NODE_API_EXPERIMENTAL_LIB}) diff --git a/PORTING.md b/PORTING.md index 657cbb3..4e64f35 100644 --- a/PORTING.md +++ b/PORTING.md @@ -68,7 +68,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h | `test_properties` | Ported ✅ | Easy | | `test_reference` | Not ported | Medium | | `test_reference_double_free` | Ported ✅ | Easy | -| `test_sharedarraybuffer` | Not ported | Medium | +| `test_sharedarraybuffer` | Ported ✅ | Medium | | `test_string` | Not ported | Medium | | `test_symbol` | Ported ✅ | Easy | | `test_typedarray` | Not ported | Medium | diff --git a/tests/js-native-api/test_sharedarraybuffer/CMakeLists.txt b/tests/js-native-api/test_sharedarraybuffer/CMakeLists.txt new file mode 100644 index 0000000..d9b8aba --- /dev/null +++ b/tests/js-native-api/test_sharedarraybuffer/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_experimental_addon(test_sharedarraybuffer test_sharedarraybuffer.c) diff --git a/tests/js-native-api/test_sharedarraybuffer/test.js b/tests/js-native-api/test_sharedarraybuffer/test.js new file mode 100644 index 0000000..a8e50c9 --- /dev/null +++ b/tests/js-native-api/test_sharedarraybuffer/test.js @@ -0,0 +1,88 @@ +"use strict"; + +if (experimentalFeatures.sharedArrayBuffer) { + const test_sharedarraybuffer = loadAddon("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" }, + ); + } +} diff --git a/tests/js-native-api/test_sharedarraybuffer/test_sharedarraybuffer.c b/tests/js-native-api/test_sharedarraybuffer/test_sharedarraybuffer.c new file mode 100644 index 0000000..661e52c --- /dev/null +++ b/tests/js-native-api/test_sharedarraybuffer/test_sharedarraybuffer.c @@ -0,0 +1,130 @@ +#define NAPI_EXPERIMENTAL +#include +#include +#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 From e65e5a4298acd7fdb1d295ba90d35974d60f9aec Mon Sep 17 00:00:00 2001 From: Balakrishna Avulapati Date: Sat, 4 Apr 2026 00:41:38 +0530 Subject: [PATCH 2/2] use skipTest and version check for experimental features Signed-off-by: Balakrishna Avulapati --- eslint.config.js | 2 + implementors/node/features.js | 8 +- implementors/node/process.js | 7 + implementors/node/tests.ts | 8 + .../test_sharedarraybuffer/test.js | 153 +++++++++--------- 5 files changed, 102 insertions(+), 76 deletions(-) create mode 100644 implementors/node/process.js diff --git a/eslint.config.js b/eslint.config.js index fff73c6..e02a875 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -17,6 +17,8 @@ export default defineConfig([ mustNotCall: "readonly", gcUntil: "readonly", experimentalFeatures: "readonly", + napiVersion: "readonly", + skipTest: "readonly", }, }, rules: { diff --git a/implementors/node/features.js b/implementors/node/features.js index d06bad9..cd46f82 100644 --- a/implementors/node/features.js +++ b/implementors/node/features.js @@ -1,8 +1,14 @@ // Declares which experimental Node-API features this runtime supports. // Each key corresponds to a NODE_API_EXPERIMENTAL_HAS_* compile-time macro. // Other implementors should set unsupported features to false or omit them. + +const [major, minor] = process.version.slice(1).split('.').map(Number); + globalThis.experimentalFeatures = { - sharedArrayBuffer: true, + // node_api_is_sharedarraybuffer and node_api_create_sharedarraybuffer were + // added in Node.js v24.9.0. Earlier versions do not export these symbols, + // causing addons that reference them to fail at dlopen time. + sharedArrayBuffer: major >= 25 || (major === 24 && minor >= 9), createObjectWithProperties: true, setPrototype: true, postFinalizer: true, diff --git a/implementors/node/process.js b/implementors/node/process.js new file mode 100644 index 0000000..b1eb4b7 --- /dev/null +++ b/implementors/node/process.js @@ -0,0 +1,7 @@ +const napiVersion = Number(process.versions.napi); + +const skipTest = () => { + process.exit(0); +}; + +Object.assign(globalThis, { napiVersion, skipTest }); diff --git a/implementors/node/tests.ts b/implementors/node/tests.ts index d4f8b2c..0811aac 100644 --- a/implementors/node/tests.ts +++ b/implementors/node/tests.ts @@ -34,6 +34,12 @@ const GC_MODULE_PATH = path.join( "node", "gc.js" ); +const PROCESS_MODULE_PATH = path.join( + ROOT_PATH, + "implementors", + "node", + "process.js" +); const MUST_CALL_MODULE_PATH = path.join( ROOT_PATH, "implementors", @@ -79,6 +85,8 @@ export function runFileInSubprocess( "--import", "file://" + GC_MODULE_PATH, "--import", + "file://" + PROCESS_MODULE_PATH, + "--import", "file://" + MUST_CALL_MODULE_PATH, filePath, ], diff --git a/tests/js-native-api/test_sharedarraybuffer/test.js b/tests/js-native-api/test_sharedarraybuffer/test.js index a8e50c9..e89e4d6 100644 --- a/tests/js-native-api/test_sharedarraybuffer/test.js +++ b/tests/js-native-api/test_sharedarraybuffer/test.js @@ -1,88 +1,91 @@ "use strict"; -if (experimentalFeatures.sharedArrayBuffer) { - const test_sharedarraybuffer = loadAddon("test_sharedarraybuffer"); +// SharedArrayBuffer support is an experimental feature. +if (!experimentalFeatures.sharedArrayBuffer) { + skipTest(); +} - { - const sab = new SharedArrayBuffer(16); - const ab = new ArrayBuffer(16); - const obj = {}; - const arr = []; +const test_sharedarraybuffer = loadAddon("test_sharedarraybuffer"); - 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, - ); - } +{ + const sab = new SharedArrayBuffer(16); + const ab = new ArrayBuffer(16); + const obj = {}; + const arr = []; - // Test node_api_create_sharedarraybuffer - { - const sab = test_sharedarraybuffer.TestCreateSharedArrayBuffer(16); - assert(sab instanceof SharedArrayBuffer); - assert.strictEqual(sab.byteLength, 16); - } + 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_get_sharedarraybuffer_info - { - const sab = new SharedArrayBuffer(32); - const byteLength = test_sharedarraybuffer.TestGetSharedArrayBufferInfo(sab); - assert.strictEqual(byteLength, 32); - } +// 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); +// 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); - } + // 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 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 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" }, - ); - } +// Test invalid arguments +{ + assert.throws( + () => { + test_sharedarraybuffer.TestGetSharedArrayBufferInfo({}); + }, + { name: "Error", message: "Invalid argument" }, + ); }