From da7001acead51751f9dba4ca46dd2e962fb7e388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sat, 28 Feb 2026 12:35:17 +0100 Subject: [PATCH 1/6] doc: add porting plan with difficulty ratings Enumerates all 58 test directories from Node.js's test/js-native-api and test/node-api, rates each by porting difficulty (Easy/Medium/Hard), and documents special considerations for tests with deep runtime dependencies (libuv, worker threads, SEA, node_api_post_finalizer, etc.). Co-Authored-By: Claude Sonnet 4.6 --- PORTING.md | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 PORTING.md diff --git a/PORTING.md b/PORTING.md new file mode 100644 index 0000000..630c1f0 --- /dev/null +++ b/PORTING.md @@ -0,0 +1,164 @@ +# Porting Plan + +This document tracks the progress of porting tests from Node.js's test suite into the CTS. +The source directories are [`test/js-native-api`](https://github.com/nodejs/node/tree/main/test/js-native-api) +and [`test/node-api`](https://github.com/nodejs/node/tree/main/test/node-api) in the Node.js repository. + +Difficulty is assessed on two axes: +- **Size/complexity** — total lines of C/C++ and JS across all source files +- **Runtime-API dependence** — pure `js_native_api.h` is cheapest; Node.js extensions and direct + libuv calls require harness work or Node-only scoping + +| Rating | Meaning | +|---|---| +| Easy | Small test, pure `js_native_api.h` or trivial runtime API, straightforward 1:1 port | +| Medium | Moderate size or uses a Node.js extension API that the harness will need to abstract | +| Hard | Large test and/or deep libuv/worker/SEA dependency; may need new harness primitives or Node-only scoping | + +## Engine-specific (`js-native-api`) + +Tests covering the engine-specific part of Node-API, defined in `js_native_api.h`. + +| Directory | Status | Difficulty | +|---|---|---| +| `2_function_arguments` | Ported | — | +| `3_callbacks` | Not ported | Easy | +| `4_object_factory` | Not ported | Easy | +| `5_function_factory` | Not ported | Easy | +| `6_object_wrap` | Not ported | Medium | +| `7_factory_wrap` | Not ported | Easy | +| `8_passing_wrapped` | Not ported | Easy | +| `test_array` | Not ported | Easy | +| `test_bigint` | Not ported | Easy | +| `test_cannot_run_js` | Not ported | Medium | +| `test_constructor` | Not ported | Medium | +| `test_conversions` | Not ported | Medium | +| `test_dataview` | Not ported | Easy | +| `test_date` | Not ported | Easy | +| `test_error` | Not ported | Medium | +| `test_exception` | Not ported | Medium | +| `test_finalizer` | Not ported | Medium | +| `test_function` | Not ported | Medium | +| `test_general` | Not ported | Hard | +| `test_handle_scope` | Not ported | Easy | +| `test_instance_data` | Not ported | Easy | +| `test_new_target` | Not ported | Easy | +| `test_number` | Not ported | Easy | +| `test_object` | Not ported | Hard | +| `test_promise` | Not ported | Easy | +| `test_properties` | Not ported | Easy | +| `test_reference` | Not ported | Medium | +| `test_reference_double_free` | Not ported | Easy | +| `test_sharedarraybuffer` | Not ported | Medium | +| `test_string` | Not ported | Medium | +| `test_symbol` | Not ported | Easy | +| `test_typedarray` | Not ported | Medium | + +## Runtime-specific (`node-api`) + +Tests covering the runtime-specific part of Node-API, defined in `node_api.h`. + +| Directory | Status | Difficulty | +|---|---|---| +| `1_hello_world` | Not ported | Easy | +| `test_async` | Not ported | Hard | +| `test_async_cleanup_hook` | Not ported | Hard | +| `test_async_context` | Not ported | Hard | +| `test_buffer` | Not ported | Medium | +| `test_callback_scope` | Not ported | Hard | +| `test_cleanup_hook` | Not ported | Medium | +| `test_env_teardown_gc` | Not ported | Easy | +| `test_exception` | Not ported | Easy | +| `test_fatal` | Not ported | Hard | +| `test_fatal_exception` | Not ported | Easy | +| `test_general` | Not ported | Medium | +| `test_init_order` | Not ported | Medium | +| `test_instance_data` | Not ported | Hard | +| `test_make_callback` | Not ported | Hard | +| `test_make_callback_recurse` | Not ported | Hard | +| `test_null_init` | Not ported | Medium | +| `test_reference_by_node_api_version` | Not ported | Medium | +| `test_sea_addon` | Not ported | Hard | +| `test_threadsafe_function` | Not ported | Hard | +| `test_threadsafe_function_shutdown` | Not ported | Hard | +| `test_uv_loop` | Not ported | Hard | +| `test_uv_threadpool_size` | Not ported | Hard | +| `test_worker_buffer_callback` | Not ported | Hard | +| `test_worker_terminate` | Not ported | Hard | +| `test_worker_terminate_finalization` | Not ported | Hard | + +## Special Considerations + +### `node_api_post_finalizer` (`6_object_wrap`, `test_finalizer`) + +Both tests call `node_api_post_finalizer` to defer JS-touching work out of the GC finalizer and +onto the main thread. This is a Node.js extension not guaranteed to be present on other engines. +The CTS harness will need a platform-agnostic post-finalizer primitive that implementors can map +to their own deferred-callback mechanism, or the tests need to isolate the post-finalizer cases +into a Node-specific subtest. + +### `node_api_set_prototype` / `node_api_get_prototype` (`test_general`, js-native-api) + +The general test suite mixes standard `js_native_api.h` assertions with calls to +`node_api_set_prototype` and `node_api_get_prototype`, which are Node.js extensions. These do +not exist on other engines. The CTS port should split the affected test cases into an engine-agnostic +core and a Node-only annex, or guard those cases with a runtime capability check. + +### SharedArrayBuffer backing-store creation (`test_sharedarraybuffer`) + +While `napi_is_sharedarraybuffer` and `napi_get_typedarray_info` are part of `js_native_api.h`, +the test creates its SharedArrayBuffer via a Node-specific helper. The CTS version will need a +harness-provided factory (something like `create_shared_array_buffer(size)`) that each runtime +can implement using its own path. + +### libuv dependency (multiple `node-api` tests) + +The following tests call into libuv directly — `napi_get_uv_event_loop`, `uv_thread_t`, +`uv_mutex_t`, `uv_async_t`, `uv_check_t`, `uv_idle_t`, `uv_queue_work`, and related APIs: + +- `test_async`, `test_async_cleanup_hook`, `test_async_context` +- `test_callback_scope` +- `test_fatal` (uses `uv_thread_t` to test cross-thread fatal errors) +- `test_instance_data` (async work + threadsafe functions + `uv_thread_t`) +- `test_uv_loop`, `test_uv_threadpool_size` + +Porting options: +1. **Node-only scope** — mark these tests as Node.js-only and skip on other runtimes. +2. **Harness abstraction** — introduce a minimal platform-agnostic threading/async API in the + harness (e.g., `cts_thread_create`, `cts_async_schedule`) that implementors back with their + own event loop primitives (libuv, tokio, etc.). + +### Threadsafe functions (`test_threadsafe_function`, `test_threadsafe_function_shutdown`) + +`test_threadsafe_function` is the largest single test (~700 total lines across C and JS), covering +blocking/non-blocking queue modes, queue-full handling, multiple concurrent threads, finalization +ordering, uncaught exception propagation, and high-precision timing via `uv_hrtime`. The threading +primitives are libuv-specific (same concern as the section above). Porting this test likely depends +on resolving the libuv abstraction question first. + +### Worker threads (`test_worker_buffer_callback`, `test_worker_terminate`, `test_worker_terminate_finalization`) + +These three tests exercise addon behavior inside Node.js worker threads: buffer finalizer delivery +in worker contexts, function-call behavior under pending exceptions during worker shutdown, and +wrapped-object finalization on forced worker termination. Node.js worker threads have no direct +equivalent in most other Node-API runtimes. These tests are likely Node.js-only and should be +scoped accordingly. + +### SEA — Single Executable Applications (`test_sea_addon`) + +`test_sea_addon` verifies that a native addon can be loaded inside a Node.js Single Executable +Application. SEA is a Node.js-specific packaging feature with no equivalent in other runtimes. +This test should be excluded from the CTS scope or placed in a Node-only annex. + +### `napi_get_node_version` (`test_general`, node-api) + +`test_general` calls `napi_get_node_version`, which returns the Node.js major/minor/patch version. +No equivalent exists in other runtimes. The CTS port should either omit that assertion or expose a +harness helper (e.g., `cts_get_runtime_version`) that runtimes can optionally implement. + +### Legacy module registration (`test_null_init`) + +`test_null_init` exercises the deprecated `NAPI_MODULE` macro with a NULL init function, calling +`napi_module_register` directly. Some newer runtimes that implement Node-API may not support this +legacy registration path. If so, this test should be scoped as Node-only or skipped on runtimes +that only support `NAPI_MODULE_INIT`. From 17a3510a00cdcf382df1e2a253631177d75888b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sat, 28 Feb 2026 13:45:06 +0100 Subject: [PATCH 2/6] feat: extend harness with assert methods, gcUntil, and --expose-gc Adds the following to the Node.js implementor and harness in preparation for porting the easy js-native-api tests: - assert.js: expose assert.ok, .strictEqual, .notStrictEqual, .deepStrictEqual, and .throws as methods on the global assert object - gc.js: new module providing a global gcUntil(name, condition) helper that drives GC until a condition is met (needed for finalizer tests) - tests.ts: inject --expose-gc and gc.js into every test subprocess - CMakeLists.txt: broaden add_node_api_cts_addon() to accept multiple source files via ARGN (needed for multi-file addons) - tests/harness/assert.js: exercise all new assert methods - tests/harness/gc.js: exercise gcUntil pass and failure paths Co-Authored-By: Claude Sonnet 4.6 --- CMakeLists.txt | 4 +-- implementors/node/assert.js | 24 +++++++++++++++--- implementors/node/gc.js | 13 ++++++++++ implementors/node/tests.ts | 9 +++++++ tests/harness/assert.js | 50 +++++++++++++++++++++++++++++++++++++ tests/harness/gc.js | 27 ++++++++++++++++++++ 6 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 implementors/node/gc.js create mode 100644 tests/harness/gc.js diff --git a/CMakeLists.txt b/CMakeLists.txt index a5a33c7..9b812d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,8 @@ if(MSVC) execute_process(COMMAND ${CMAKE_AR} /def:${NODE_API_DEF} /out:${NODE_API_LIB} ${CMAKE_STATIC_LINKER_FLAGS}) endif() -function(add_node_api_cts_addon ADDON_NAME SRC) - add_library(${ADDON_NAME} SHARED ${SRC}) +function(add_node_api_cts_addon ADDON_NAME) + add_library(${ADDON_NAME} SHARED ${ARGN}) set_target_properties(${ADDON_NAME} PROPERTIES PREFIX "" SUFFIX ".node" diff --git a/implementors/node/assert.js b/implementors/node/assert.js index 8a5039f..de9934c 100644 --- a/implementors/node/assert.js +++ b/implementors/node/assert.js @@ -1,8 +1,24 @@ -import { ok } from "node:assert/strict"; +import { + ok, + strictEqual, + notStrictEqual, + deepStrictEqual, + throws, +} from "node:assert/strict"; -const assert = (value, message) => { - ok(value, message); -}; +const assert = Object.assign( + (value, message) => ok(value, message), + { + ok: (value, message) => ok(value, message), + strictEqual: (actual, expected, message) => + strictEqual(actual, expected, message), + notStrictEqual: (actual, expected, message) => + notStrictEqual(actual, expected, message), + deepStrictEqual: (actual, expected, message) => + deepStrictEqual(actual, expected, message), + throws: (fn, error, message) => throws(fn, error, message), + }, +); Object.assign(globalThis, { assert }); diff --git a/implementors/node/gc.js b/implementors/node/gc.js new file mode 100644 index 0000000..791cb73 --- /dev/null +++ b/implementors/node/gc.js @@ -0,0 +1,13 @@ +const gcUntil = async (name, condition) => { + let count = 0; + while (!condition()) { + await new Promise((resolve) => setImmediate(resolve)); + if (++count < 10) { + globalThis.gc(); + } else { + throw new Error(`GC test "${name}" failed after ${count} attempts`); + } + } +}; + +Object.assign(globalThis, { gcUntil }); diff --git a/implementors/node/tests.ts b/implementors/node/tests.ts index 28dfa54..d39c64b 100644 --- a/implementors/node/tests.ts +++ b/implementors/node/tests.ts @@ -22,6 +22,12 @@ const LOAD_ADDON_MODULE_PATH = path.join( "node", "load-addon.js" ); +const GC_MODULE_PATH = path.join( + ROOT_PATH, + "implementors", + "node", + "gc.js" +); export function listDirectoryEntries(dir: string) { const entries = fs.readdirSync(dir, { withFileTypes: true }); @@ -51,10 +57,13 @@ export function runFileInSubprocess( process.execPath, [ // Using file scheme prefix when to enable imports on Windows + "--expose-gc", "--import", "file://" + ASSERT_MODULE_PATH, "--import", "file://" + LOAD_ADDON_MODULE_PATH, + "--import", + "file://" + GC_MODULE_PATH, filePath, ], { cwd } diff --git a/tests/harness/assert.js b/tests/harness/assert.js index 47b89f3..9abf943 100644 --- a/tests/harness/assert.js +++ b/tests/harness/assert.js @@ -31,3 +31,53 @@ try { if (!threw) { throw new Error('Global assert(false, message) must throw'); } + +// assert.ok +if (typeof assert.ok !== 'function') { + throw new Error('Expected assert.ok to be a function'); +} +assert.ok(true); +threw = false; +try { assert.ok(false); } catch { threw = true; } +if (!threw) throw new Error('assert.ok(false) must throw'); + +// assert.strictEqual +if (typeof assert.strictEqual !== 'function') { + throw new Error('Expected assert.strictEqual to be a function'); +} +assert.strictEqual(1, 1); +assert.strictEqual('a', 'a'); +assert.strictEqual(NaN, NaN); // uses Object.is semantics +threw = false; +try { assert.strictEqual(1, 2); } catch { threw = true; } +if (!threw) throw new Error('assert.strictEqual(1, 2) must throw'); + +// assert.notStrictEqual +if (typeof assert.notStrictEqual !== 'function') { + throw new Error('Expected assert.notStrictEqual to be a function'); +} +assert.notStrictEqual(1, 2); +assert.notStrictEqual('a', 'b'); +threw = false; +try { assert.notStrictEqual(1, 1); } catch { threw = true; } +if (!threw) throw new Error('assert.notStrictEqual(1, 1) must throw'); + +// assert.deepStrictEqual +if (typeof assert.deepStrictEqual !== 'function') { + throw new Error('Expected assert.deepStrictEqual to be a function'); +} +assert.deepStrictEqual({ a: 1 }, { a: 1 }); +assert.deepStrictEqual([1, 2, 3], [1, 2, 3]); +threw = false; +try { assert.deepStrictEqual({ a: 1 }, { a: 2 }); } catch { threw = true; } +if (!threw) throw new Error('assert.deepStrictEqual({ a: 1 }, { a: 2 }) must throw'); + +// assert.throws +if (typeof assert.throws !== 'function') { + throw new Error('Expected assert.throws to be a function'); +} +assert.throws(() => { throw new Error('oops'); }, /oops/); +assert.throws(() => { throw new TypeError('bad'); }, TypeError); +threw = false; +try { assert.throws(() => { /* does not throw */ }); } catch { threw = true; } +if (!threw) throw new Error('assert.throws must throw when fn does not throw'); diff --git a/tests/harness/gc.js b/tests/harness/gc.js new file mode 100644 index 0000000..de7d570 --- /dev/null +++ b/tests/harness/gc.js @@ -0,0 +1,27 @@ +if (typeof gcUntil !== 'function') { + throw new Error('Expected a global gcUntil function'); +} + +// gcUntil should resolve once the condition becomes true +let count = 0; +await gcUntil('test-passes', () => { + count++; + return count >= 2; +}); +if (count < 2) { + throw new Error(`Expected condition to be checked at least twice, got ${count}`); +} + +// gcUntil should throw after exhausting retries when condition never becomes true +let threw = false; +try { + await gcUntil('test-fails', () => false); +} catch (error) { + threw = true; + if (!error.message.includes('test-fails')) { + throw new Error(`Expected error message to include 'test-fails' but got: ${error.message}`); + } +} +if (!threw) { + throw new Error('gcUntil must throw when the condition never becomes true'); +} From 1d1e3f09a03f6cbb3fcc17a282f98f9e11163e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sat, 28 Feb 2026 13:59:41 +0100 Subject: [PATCH 3/6] feat: port 17 easy js-native-api tests to CTS Port all "Easy" difficulty engine-specific tests from Node.js: 3_callbacks, 4_object_factory, 5_function_factory, 7_factory_wrap, 8_passing_wrapped, test_array, test_bigint, test_dataview, test_date, test_handle_scope, test_instance_data, test_new_target, test_number, test_promise, test_properties, test_reference_double_free, test_symbol. C/C++ sources are copied from Node.js with minimal changes (entry_point.h macro). JS tests are adapted to use CTS globals (loadAddon, assert, gcUntil) instead of Node.js test harness (common.js, require). test_dataview: removed SharedArrayBuffer tests that depend on experimental node_api_is_sharedarraybuffer API (not in stable node-api-headers). Also updates node-api-headers from 1.7.0 to 1.8.0. Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 911 +----------------- package.json | 2 +- tests/js-native-api/3_callbacks/3_callbacks.c | 58 ++ .../js-native-api/3_callbacks/CMakeLists.txt | 1 + tests/js-native-api/3_callbacks/test.js | 26 + .../4_object_factory/4_object_factory.c | 24 + .../4_object_factory/CMakeLists.txt | 1 + tests/js-native-api/4_object_factory/test.js | 6 + .../5_function_factory/5_function_factory.c | 24 + .../5_function_factory/CMakeLists.txt | 1 + .../js-native-api/5_function_factory/test.js | 5 + .../7_factory_wrap/7_factory_wrap.cc | 32 + .../7_factory_wrap/CMakeLists.txt | 1 + .../js-native-api/7_factory_wrap/myobject.cc | 101 ++ tests/js-native-api/7_factory_wrap/myobject.h | 27 + tests/js-native-api/7_factory_wrap/test.js | 24 + .../8_passing_wrapped/8_passing_wrapped.cc | 61 ++ .../8_passing_wrapped/CMakeLists.txt | 1 + .../8_passing_wrapped/myobject.cc | 80 ++ .../8_passing_wrapped/myobject.h | 26 + tests/js-native-api/8_passing_wrapped/test.js | 12 + tests/js-native-api/test_array/CMakeLists.txt | 1 + tests/js-native-api/test_array/test.js | 55 ++ tests/js-native-api/test_array/test_array.c | 188 ++++ .../js-native-api/test_bigint/CMakeLists.txt | 1 + tests/js-native-api/test_bigint/test.js | 50 + tests/js-native-api/test_bigint/test_bigint.c | 159 +++ .../test_dataview/CMakeLists.txt | 1 + tests/js-native-api/test_dataview/test.js | 21 + .../test_dataview/test_dataview.c | 104 ++ tests/js-native-api/test_date/CMakeLists.txt | 1 + tests/js-native-api/test_date/test.js | 15 + tests/js-native-api/test_date/test_date.c | 64 ++ .../test_handle_scope/CMakeLists.txt | 1 + tests/js-native-api/test_handle_scope/test.js | 14 + .../test_handle_scope/test_handle_scope.c | 86 ++ .../test_instance_data/CMakeLists.txt | 1 + .../js-native-api/test_instance_data/test.js | 5 + .../test_instance_data/test_instance_data.c | 96 ++ .../test_new_target/CMakeLists.txt | 1 + tests/js-native-api/test_new_target/test.js | 18 + .../test_new_target/test_new_target.c | 70 ++ .../js-native-api/test_number/CMakeLists.txt | 1 + tests/js-native-api/test_number/test.js | 131 +++ tests/js-native-api/test_number/test_null.c | 77 ++ tests/js-native-api/test_number/test_null.h | 8 + tests/js-native-api/test_number/test_null.js | 16 + tests/js-native-api/test_number/test_number.c | 110 +++ .../js-native-api/test_promise/CMakeLists.txt | 1 + tests/js-native-api/test_promise/test.js | 72 ++ .../js-native-api/test_promise/test_promise.c | 64 ++ .../test_properties/CMakeLists.txt | 1 + tests/js-native-api/test_properties/test.js | 66 ++ .../test_properties/test_properties.c | 113 +++ .../test_reference_double_free/CMakeLists.txt | 1 + .../test_reference_double_free/test.js | 9 + .../test_reference_double_free.c | 90 ++ .../test_reference_double_free/test_wrap.js | 8 + .../js-native-api/test_symbol/CMakeLists.txt | 1 + tests/js-native-api/test_symbol/test1.js | 15 + tests/js-native-api/test_symbol/test2.js | 13 + tests/js-native-api/test_symbol/test3.js | 15 + tests/js-native-api/test_symbol/test_symbol.c | 38 + 63 files changed, 2320 insertions(+), 906 deletions(-) create mode 100644 tests/js-native-api/3_callbacks/3_callbacks.c create mode 100644 tests/js-native-api/3_callbacks/CMakeLists.txt create mode 100644 tests/js-native-api/3_callbacks/test.js create mode 100644 tests/js-native-api/4_object_factory/4_object_factory.c create mode 100644 tests/js-native-api/4_object_factory/CMakeLists.txt create mode 100644 tests/js-native-api/4_object_factory/test.js create mode 100644 tests/js-native-api/5_function_factory/5_function_factory.c create mode 100644 tests/js-native-api/5_function_factory/CMakeLists.txt create mode 100644 tests/js-native-api/5_function_factory/test.js create mode 100644 tests/js-native-api/7_factory_wrap/7_factory_wrap.cc create mode 100644 tests/js-native-api/7_factory_wrap/CMakeLists.txt create mode 100644 tests/js-native-api/7_factory_wrap/myobject.cc create mode 100644 tests/js-native-api/7_factory_wrap/myobject.h create mode 100644 tests/js-native-api/7_factory_wrap/test.js create mode 100644 tests/js-native-api/8_passing_wrapped/8_passing_wrapped.cc create mode 100644 tests/js-native-api/8_passing_wrapped/CMakeLists.txt create mode 100644 tests/js-native-api/8_passing_wrapped/myobject.cc create mode 100644 tests/js-native-api/8_passing_wrapped/myobject.h create mode 100644 tests/js-native-api/8_passing_wrapped/test.js create mode 100644 tests/js-native-api/test_array/CMakeLists.txt create mode 100644 tests/js-native-api/test_array/test.js create mode 100644 tests/js-native-api/test_array/test_array.c create mode 100644 tests/js-native-api/test_bigint/CMakeLists.txt create mode 100644 tests/js-native-api/test_bigint/test.js create mode 100644 tests/js-native-api/test_bigint/test_bigint.c create mode 100644 tests/js-native-api/test_dataview/CMakeLists.txt create mode 100644 tests/js-native-api/test_dataview/test.js create mode 100644 tests/js-native-api/test_dataview/test_dataview.c create mode 100644 tests/js-native-api/test_date/CMakeLists.txt create mode 100644 tests/js-native-api/test_date/test.js create mode 100644 tests/js-native-api/test_date/test_date.c create mode 100644 tests/js-native-api/test_handle_scope/CMakeLists.txt create mode 100644 tests/js-native-api/test_handle_scope/test.js create mode 100644 tests/js-native-api/test_handle_scope/test_handle_scope.c create mode 100644 tests/js-native-api/test_instance_data/CMakeLists.txt create mode 100644 tests/js-native-api/test_instance_data/test.js create mode 100644 tests/js-native-api/test_instance_data/test_instance_data.c create mode 100644 tests/js-native-api/test_new_target/CMakeLists.txt create mode 100644 tests/js-native-api/test_new_target/test.js create mode 100644 tests/js-native-api/test_new_target/test_new_target.c create mode 100644 tests/js-native-api/test_number/CMakeLists.txt create mode 100644 tests/js-native-api/test_number/test.js create mode 100644 tests/js-native-api/test_number/test_null.c create mode 100644 tests/js-native-api/test_number/test_null.h create mode 100644 tests/js-native-api/test_number/test_null.js create mode 100644 tests/js-native-api/test_number/test_number.c create mode 100644 tests/js-native-api/test_promise/CMakeLists.txt create mode 100644 tests/js-native-api/test_promise/test.js create mode 100644 tests/js-native-api/test_promise/test_promise.c create mode 100644 tests/js-native-api/test_properties/CMakeLists.txt create mode 100644 tests/js-native-api/test_properties/test.js create mode 100644 tests/js-native-api/test_properties/test_properties.c create mode 100644 tests/js-native-api/test_reference_double_free/CMakeLists.txt create mode 100644 tests/js-native-api/test_reference_double_free/test.js create mode 100644 tests/js-native-api/test_reference_double_free/test_reference_double_free.c create mode 100644 tests/js-native-api/test_reference_double_free/test_wrap.js create mode 100644 tests/js-native-api/test_symbol/CMakeLists.txt create mode 100644 tests/js-native-api/test_symbol/test1.js create mode 100644 tests/js-native-api/test_symbol/test2.js create mode 100644 tests/js-native-api/test_symbol/test3.js create mode 100644 tests/js-native-api/test_symbol/test_symbol.c diff --git a/package-lock.json b/package-lock.json index 3007845..39e6df5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,8 @@ }, "devDependencies": { "@types/node": "^24.10.1", - "cmake-js": "^7.4.0", "eslint": "^9.39.1", - "node-api-headers": "^1.7.0" + "node-api-headers": "^1.8.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -256,6 +255,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -299,16 +299,6 @@ "node": ">=22" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -325,28 +315,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/aproba": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", - "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", - "dev": true, - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -354,25 +322,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -391,20 +340,6 @@ "concat-map": "0.0.1" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -432,58 +367,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cmake-js": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-7.4.0.tgz", - "integrity": "sha512-Lw0JxEHrmk+qNj1n9W9d4IvkDdYTBn7l2BW6XmtLj7WPpIo2shvxUy+YokfjMxAAOELNonQwX3stkPhM5xSC2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "axios": "^1.6.5", - "debug": "^4", - "fs-extra": "^11.2.0", - "memory-stream": "^1.0.0", - "node-api-headers": "^1.1.0", - "npmlog": "^6.0.2", - "rc": "^1.2.7", - "semver": "^7.5.4", - "tar": "^6.2.0", - "url-join": "^4.0.1", - "which": "^2.0.2", - "yargs": "^17.7.2" - }, - "bin": { - "cmake-js": "bin/cmake-js" - }, - "engines": { - "node": ">= 14.15.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -504,29 +387,6 @@ "dev": true, "license": "MIT" }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -534,13 +394,6 @@ "dev": true, "license": "MIT" }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, - "license": "ISC" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -574,16 +427,6 @@ } } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -591,104 +434,6 @@ "dev": true, "license": "MIT" }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -708,6 +453,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -928,165 +674,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs-extra": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", - "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1113,26 +700,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1143,55 +710,6 @@ "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1229,20 +747,6 @@ "node": ">=0.8.19" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1253,16 +757,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1317,19 +811,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -1377,49 +858,6 @@ "dev": true, "license": "MIT" }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/memory-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-1.0.0.tgz", - "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "^3.4.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1433,66 +871,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1508,29 +886,12 @@ "license": "MIT" }, "node_modules/node-api-headers": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.7.0.tgz", - "integrity": "sha512-uJMGdkhVwu9+I3UsVvI3KW6ICAy/yDfsu5Br9rSnTtY3WpoaComXvKloiV5wtx0Md2rn0B9n29Ys2WMNwWxj9A==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.8.0.tgz", + "integrity": "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==", "dev": true, "license": "MIT" }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1624,13 +985,6 @@ "node": ">= 0.8.0" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "license": "MIT" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1641,47 +995,6 @@ "node": ">=6" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1692,47 +1005,6 @@ "node": ">=4" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "license": "ISC" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1756,61 +1028,6 @@ "node": ">=8" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -1824,24 +1041,6 @@ "node": ">=8" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1862,16 +1061,6 @@ "dev": true, "license": "MIT" }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1882,20 +1071,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true, - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1912,16 +1087,6 @@ "node": ">= 8" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -1932,70 +1097,6 @@ "node": ">=0.10.0" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 1590d7b..8ef19e8 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "devDependencies": { "@types/node": "^24.10.1", "eslint": "^9.39.1", - "node-api-headers": "^1.7.0" + "node-api-headers": "^1.8.0" }, "dependencies": { "amaro": "^1.1.5" diff --git a/tests/js-native-api/3_callbacks/3_callbacks.c b/tests/js-native-api/3_callbacks/3_callbacks.c new file mode 100644 index 0000000..44bd245 --- /dev/null +++ b/tests/js-native-api/3_callbacks/3_callbacks.c @@ -0,0 +1,58 @@ +#include +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value RunCallback(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NODE_API_ASSERT(env, argc == 1, + "Wrong number of arguments. Expects a single argument."); + + napi_valuetype valuetype0; + NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0)); + NODE_API_ASSERT(env, valuetype0 == napi_function, + "Wrong type of arguments. Expects a function as first argument."); + + napi_valuetype valuetype1; + NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1)); + NODE_API_ASSERT(env, valuetype1 == napi_undefined, + "Additional arguments should be undefined."); + + napi_value argv[1]; + const char* str = "hello world"; + size_t str_len = strlen(str); + NODE_API_CALL(env, napi_create_string_utf8(env, str, str_len, argv)); + + napi_value global; + NODE_API_CALL(env, napi_get_global(env, &global)); + + napi_value cb = args[0]; + NODE_API_CALL(env, napi_call_function(env, global, cb, 1, argv, NULL)); + + return NULL; +} + +static napi_value RunCallbackWithRecv(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + napi_value cb = args[0]; + napi_value recv = args[1]; + NODE_API_CALL(env, napi_call_function(env, recv, cb, 0, NULL, NULL)); + return NULL; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor desc[2] = { + DECLARE_NODE_API_PROPERTY("RunCallback", RunCallback), + DECLARE_NODE_API_PROPERTY("RunCallbackWithRecv", RunCallbackWithRecv), + }; + NODE_API_CALL(env, napi_define_properties(env, exports, 2, desc)); + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/3_callbacks/CMakeLists.txt b/tests/js-native-api/3_callbacks/CMakeLists.txt new file mode 100644 index 0000000..b7b0aa3 --- /dev/null +++ b/tests/js-native-api/3_callbacks/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(3_callbacks 3_callbacks.c) diff --git a/tests/js-native-api/3_callbacks/test.js b/tests/js-native-api/3_callbacks/test.js new file mode 100644 index 0000000..8dfc464 --- /dev/null +++ b/tests/js-native-api/3_callbacks/test.js @@ -0,0 +1,26 @@ +'use strict'; +const addon = loadAddon('3_callbacks'); + +let called = false; +addon.RunCallback((msg) => { + assert.strictEqual(msg, 'hello world'); + called = true; +}); +assert(called); + +function testRecv(desiredRecv) { + let recvCalled = false; + addon.RunCallbackWithRecv(function() { + assert.strictEqual(this, desiredRecv); + recvCalled = true; + }, desiredRecv); + assert(recvCalled); +} + +testRecv(undefined); +testRecv(null); +testRecv(5); +testRecv(true); +testRecv('Hello'); +testRecv([]); +testRecv({}); diff --git a/tests/js-native-api/4_object_factory/4_object_factory.c b/tests/js-native-api/4_object_factory/4_object_factory.c new file mode 100644 index 0000000..8fd6090 --- /dev/null +++ b/tests/js-native-api/4_object_factory/4_object_factory.c @@ -0,0 +1,24 @@ +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value CreateObject(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)); + + napi_value obj; + NODE_API_CALL(env, napi_create_object(env, &obj)); + + NODE_API_CALL(env, napi_set_named_property(env, obj, "msg", args[0])); + + return obj; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + NODE_API_CALL(env, + napi_create_function(env, "exports", -1, CreateObject, NULL, &exports)); + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/4_object_factory/CMakeLists.txt b/tests/js-native-api/4_object_factory/CMakeLists.txt new file mode 100644 index 0000000..8beff02 --- /dev/null +++ b/tests/js-native-api/4_object_factory/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(4_object_factory 4_object_factory.c) diff --git a/tests/js-native-api/4_object_factory/test.js b/tests/js-native-api/4_object_factory/test.js new file mode 100644 index 0000000..179e91c --- /dev/null +++ b/tests/js-native-api/4_object_factory/test.js @@ -0,0 +1,6 @@ +'use strict'; +const addon = loadAddon('4_object_factory'); + +const obj1 = addon('hello'); +const obj2 = addon('world'); +assert.strictEqual(`${obj1.msg} ${obj2.msg}`, 'hello world'); diff --git a/tests/js-native-api/5_function_factory/5_function_factory.c b/tests/js-native-api/5_function_factory/5_function_factory.c new file mode 100644 index 0000000..8c2bdac --- /dev/null +++ b/tests/js-native-api/5_function_factory/5_function_factory.c @@ -0,0 +1,24 @@ +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value MyFunction(napi_env env, napi_callback_info info) { + napi_value str; + NODE_API_CALL(env, napi_create_string_utf8(env, "hello world", -1, &str)); + return str; +} + +static napi_value CreateFunction(napi_env env, napi_callback_info info) { + napi_value fn; + NODE_API_CALL(env, + napi_create_function(env, "theFunction", -1, MyFunction, NULL, &fn)); + return fn; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + NODE_API_CALL(env, + napi_create_function(env, "exports", -1, CreateFunction, NULL, &exports)); + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/5_function_factory/CMakeLists.txt b/tests/js-native-api/5_function_factory/CMakeLists.txt new file mode 100644 index 0000000..30aaa31 --- /dev/null +++ b/tests/js-native-api/5_function_factory/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(5_function_factory 5_function_factory.c) diff --git a/tests/js-native-api/5_function_factory/test.js b/tests/js-native-api/5_function_factory/test.js new file mode 100644 index 0000000..6741a8a --- /dev/null +++ b/tests/js-native-api/5_function_factory/test.js @@ -0,0 +1,5 @@ +'use strict'; +const addon = loadAddon('5_function_factory'); + +const fn = addon(); +assert.strictEqual(fn(), 'hello world'); diff --git a/tests/js-native-api/7_factory_wrap/7_factory_wrap.cc b/tests/js-native-api/7_factory_wrap/7_factory_wrap.cc new file mode 100644 index 0000000..5fb7a66 --- /dev/null +++ b/tests/js-native-api/7_factory_wrap/7_factory_wrap.cc @@ -0,0 +1,32 @@ +#include +#include "../common.h" +#include "../entry_point.h" +#include "myobject.h" + +napi_value CreateObject(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, nullptr, nullptr)); + + napi_value instance; + NODE_API_CALL(env, MyObject::NewInstance(env, args[0], &instance)); + + return instance; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + NODE_API_CALL(env, MyObject::Init(env)); + + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_GETTER("finalizeCount", MyObject::GetFinalizeCount), + DECLARE_NODE_API_PROPERTY("createObject", CreateObject), + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/7_factory_wrap/CMakeLists.txt b/tests/js-native-api/7_factory_wrap/CMakeLists.txt new file mode 100644 index 0000000..bfa00f1 --- /dev/null +++ b/tests/js-native-api/7_factory_wrap/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(7_factory_wrap 7_factory_wrap.cc myobject.cc) diff --git a/tests/js-native-api/7_factory_wrap/myobject.cc b/tests/js-native-api/7_factory_wrap/myobject.cc new file mode 100644 index 0000000..a1a0097 --- /dev/null +++ b/tests/js-native-api/7_factory_wrap/myobject.cc @@ -0,0 +1,101 @@ +#include "myobject.h" +#include "../common.h" + +static int finalize_count = 0; + +MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {} + +MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); } + +void MyObject::Destructor(napi_env env, + void* nativeObject, + void* /*finalize_hint*/) { + ++finalize_count; + MyObject* obj = static_cast(nativeObject); + delete obj; +} + +napi_value MyObject::GetFinalizeCount(napi_env env, napi_callback_info info) { + napi_value result; + NODE_API_CALL(env, napi_create_int32(env, finalize_count, &result)); + return result; +} + +napi_ref MyObject::constructor; + +napi_status MyObject::Init(napi_env env) { + napi_status status; + napi_property_descriptor properties[] = { + DECLARE_NODE_API_PROPERTY("plusOne", PlusOne), + }; + + napi_value cons; + status = napi_define_class( + env, "MyObject", -1, New, nullptr, 1, properties, &cons); + if (status != napi_ok) return status; + + status = napi_create_reference(env, cons, 1, &constructor); + if (status != napi_ok) return status; + + return napi_ok; +} + +napi_value MyObject::New(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_value _this; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr)); + + napi_valuetype valuetype; + NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype)); + + MyObject* obj = new MyObject(); + + if (valuetype == napi_undefined) { + obj->counter_ = 0; + } else { + NODE_API_CALL(env, napi_get_value_uint32(env, args[0], &obj->counter_)); + } + + obj->env_ = env; + NODE_API_CALL(env, + napi_wrap( + env, _this, obj, MyObject::Destructor, nullptr /* finalize_hint */, + &obj->wrapper_)); + + return _this; +} + +napi_status MyObject::NewInstance(napi_env env, + napi_value arg, + napi_value* instance) { + napi_status status; + + const int argc = 1; + napi_value argv[argc] = {arg}; + + napi_value cons; + status = napi_get_reference_value(env, constructor, &cons); + if (status != napi_ok) return status; + + status = napi_new_instance(env, cons, argc, argv, instance); + if (status != napi_ok) return status; + + return napi_ok; +} + +napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) { + napi_value _this; + NODE_API_CALL(env, + napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + MyObject* obj; + NODE_API_CALL(env, napi_unwrap(env, _this, reinterpret_cast(&obj))); + + obj->counter_ += 1; + + napi_value num; + NODE_API_CALL(env, napi_create_uint32(env, obj->counter_, &num)); + + return num; +} diff --git a/tests/js-native-api/7_factory_wrap/myobject.h b/tests/js-native-api/7_factory_wrap/myobject.h new file mode 100644 index 0000000..455ec1c --- /dev/null +++ b/tests/js-native-api/7_factory_wrap/myobject.h @@ -0,0 +1,27 @@ +#ifndef TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_ +#define TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_ + +#include + +class MyObject { + public: + static napi_status Init(napi_env env); + static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); + static napi_value GetFinalizeCount(napi_env env, napi_callback_info info); + static napi_status NewInstance(napi_env env, + napi_value arg, + napi_value* instance); + + private: + MyObject(); + ~MyObject(); + + static napi_ref constructor; + static napi_value New(napi_env env, napi_callback_info info); + static napi_value PlusOne(napi_env env, napi_callback_info info); + uint32_t counter_; + napi_env env_; + napi_ref wrapper_; +}; + +#endif // TEST_JS_NATIVE_API_7_FACTORY_WRAP_MYOBJECT_H_ diff --git a/tests/js-native-api/7_factory_wrap/test.js b/tests/js-native-api/7_factory_wrap/test.js new file mode 100644 index 0000000..de3723b --- /dev/null +++ b/tests/js-native-api/7_factory_wrap/test.js @@ -0,0 +1,24 @@ +'use strict'; +const test = loadAddon('7_factory_wrap'); + +assert.strictEqual(test.finalizeCount, 0); + +async function runGCTests() { + (() => { + const obj = test.createObject(10); + assert.strictEqual(obj.plusOne(), 11); + assert.strictEqual(obj.plusOne(), 12); + assert.strictEqual(obj.plusOne(), 13); + })(); + await gcUntil('test 1', () => (test.finalizeCount === 1)); + + (() => { + const obj2 = test.createObject(20); + assert.strictEqual(obj2.plusOne(), 21); + assert.strictEqual(obj2.plusOne(), 22); + assert.strictEqual(obj2.plusOne(), 23); + })(); + await gcUntil('test 2', () => (test.finalizeCount === 2)); +} + +await runGCTests(); diff --git a/tests/js-native-api/8_passing_wrapped/8_passing_wrapped.cc b/tests/js-native-api/8_passing_wrapped/8_passing_wrapped.cc new file mode 100644 index 0000000..1a3e6d1 --- /dev/null +++ b/tests/js-native-api/8_passing_wrapped/8_passing_wrapped.cc @@ -0,0 +1,61 @@ +#include +#include "../common.h" +#include "../entry_point.h" +#include "myobject.h" + +extern size_t finalize_count; + +static napi_value CreateObject(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, nullptr, nullptr)); + + napi_value instance; + NODE_API_CALL(env, MyObject::NewInstance(env, args[0], &instance)); + + return instance; +} + +static napi_value Add(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NODE_API_CALL(env, + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + MyObject* obj1; + NODE_API_CALL(env, + napi_unwrap(env, args[0], reinterpret_cast(&obj1))); + + MyObject* obj2; + NODE_API_CALL(env, + napi_unwrap(env, args[1], reinterpret_cast(&obj2))); + + napi_value sum; + NODE_API_CALL(env, napi_create_double(env, obj1->Val() + obj2->Val(), &sum)); + + return sum; +} + +static napi_value FinalizeCount(napi_env env, napi_callback_info info) { + napi_value return_value; + NODE_API_CALL(env, napi_create_uint32(env, finalize_count, &return_value)); + return return_value; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + MyObject::Init(env); + + napi_property_descriptor desc[] = { + DECLARE_NODE_API_PROPERTY("createObject", CreateObject), + DECLARE_NODE_API_PROPERTY("add", Add), + DECLARE_NODE_API_PROPERTY("finalizeCount", FinalizeCount), + }; + + NODE_API_CALL(env, + napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/8_passing_wrapped/CMakeLists.txt b/tests/js-native-api/8_passing_wrapped/CMakeLists.txt new file mode 100644 index 0000000..952d4d2 --- /dev/null +++ b/tests/js-native-api/8_passing_wrapped/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(8_passing_wrapped 8_passing_wrapped.cc myobject.cc) diff --git a/tests/js-native-api/8_passing_wrapped/myobject.cc b/tests/js-native-api/8_passing_wrapped/myobject.cc new file mode 100644 index 0000000..6ca61bb --- /dev/null +++ b/tests/js-native-api/8_passing_wrapped/myobject.cc @@ -0,0 +1,80 @@ +#include "myobject.h" +#include "../common.h" + +size_t finalize_count = 0; + +MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {} + +MyObject::~MyObject() { + finalize_count++; + napi_delete_reference(env_, wrapper_); +} + +void MyObject::Destructor( + napi_env env, void* nativeObject, void* /*finalize_hint*/) { + MyObject* obj = static_cast(nativeObject); + delete obj; +} + +napi_ref MyObject::constructor; + +napi_status MyObject::Init(napi_env env) { + napi_status status; + + napi_value cons; + status = napi_define_class( + env, "MyObject", -1, New, nullptr, 0, nullptr, &cons); + if (status != napi_ok) return status; + + status = napi_create_reference(env, cons, 1, &constructor); + if (status != napi_ok) return status; + + return napi_ok; +} + +napi_value MyObject::New(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + napi_value _this; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr)); + + MyObject* obj = new MyObject(); + + napi_valuetype valuetype; + NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype)); + + if (valuetype == napi_undefined) { + obj->val_ = 0; + } else { + NODE_API_CALL(env, napi_get_value_double(env, args[0], &obj->val_)); + } + + obj->env_ = env; + + // The below call to napi_wrap() must request a reference to the wrapped + // object via the out-parameter, because this ensures that we test the code + // path that deals with a reference that is destroyed from its own finalizer. + NODE_API_CALL(env, + napi_wrap(env, _this, obj, MyObject::Destructor, + nullptr /* finalize_hint */, &obj->wrapper_)); + + return _this; +} + +napi_status MyObject::NewInstance(napi_env env, + napi_value arg, + napi_value* instance) { + napi_status status; + + const int argc = 1; + napi_value argv[argc] = {arg}; + + napi_value cons; + status = napi_get_reference_value(env, constructor, &cons); + if (status != napi_ok) return status; + + status = napi_new_instance(env, cons, argc, argv, instance); + if (status != napi_ok) return status; + + return napi_ok; +} diff --git a/tests/js-native-api/8_passing_wrapped/myobject.h b/tests/js-native-api/8_passing_wrapped/myobject.h new file mode 100644 index 0000000..445cf56 --- /dev/null +++ b/tests/js-native-api/8_passing_wrapped/myobject.h @@ -0,0 +1,26 @@ +#ifndef TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_ +#define TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_ + +#include + +class MyObject { + public: + static napi_status Init(napi_env env); + static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); + static napi_status NewInstance(napi_env env, + napi_value arg, + napi_value* instance); + double Val() const { return val_; } + + private: + MyObject(); + ~MyObject(); + + static napi_ref constructor; + static napi_value New(napi_env env, napi_callback_info info); + double val_; + napi_env env_; + napi_ref wrapper_; +}; + +#endif // TEST_JS_NATIVE_API_8_PASSING_WRAPPED_MYOBJECT_H_ diff --git a/tests/js-native-api/8_passing_wrapped/test.js b/tests/js-native-api/8_passing_wrapped/test.js new file mode 100644 index 0000000..6197d31 --- /dev/null +++ b/tests/js-native-api/8_passing_wrapped/test.js @@ -0,0 +1,12 @@ +'use strict'; +const addon = loadAddon('8_passing_wrapped'); + +let obj1 = addon.createObject(10); +let obj2 = addon.createObject(20); +const result = addon.add(obj1, obj2); +assert.strictEqual(result, 30); + +// Make sure the native destructor gets called. +obj1 = null; +obj2 = null; +await gcUntil('8_passing_wrapped', () => (addon.finalizeCount() === 2)); diff --git a/tests/js-native-api/test_array/CMakeLists.txt b/tests/js-native-api/test_array/CMakeLists.txt new file mode 100644 index 0000000..277b74a --- /dev/null +++ b/tests/js-native-api/test_array/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_array test_array.c) diff --git a/tests/js-native-api/test_array/test.js b/tests/js-native-api/test_array/test.js new file mode 100644 index 0000000..d20c0a8 --- /dev/null +++ b/tests/js-native-api/test_array/test.js @@ -0,0 +1,55 @@ +'use strict'; +const test_array = loadAddon('test_array'); + +const array = [ + 1, + 9, + 48, + 13493, + 9459324, + { name: 'hello' }, + [ + 'world', + 'node', + 'abi', + ], +]; + +assert.throws( + () => { + test_array.TestGetElement(array, array.length + 1); + }, + /^Error: assertion \(\(\(uint32_t\)index < length\)\) failed: Index out of bounds!$/, +); + +assert.throws( + () => { + test_array.TestGetElement(array, -2); + }, + /^Error: assertion \(index >= 0\) failed: Invalid index\. Expects a positive integer\.$/, +); + +array.forEach(function(element, index) { + assert.strictEqual(test_array.TestGetElement(array, index), element); +}); + +assert.deepStrictEqual(test_array.New(array), array); + +assert(test_array.TestHasElement(array, 0)); +assert.strictEqual(test_array.TestHasElement(array, array.length + 1), false); + +assert(test_array.NewWithLength(0) instanceof Array); +assert(test_array.NewWithLength(1) instanceof Array); +// Check max allowed length for an array 2^32 -1 +assert(test_array.NewWithLength(4294967295) instanceof Array); + +{ + // Verify that array elements can be deleted. + const arr = ['a', 'b', 'c', 'd']; + + assert.strictEqual(arr.length, 4); + assert.strictEqual(2 in arr, true); + assert.strictEqual(test_array.TestDeleteElement(arr, 2), true); + assert.strictEqual(arr.length, 4); + assert.strictEqual(2 in arr, false); +} diff --git a/tests/js-native-api/test_array/test_array.c b/tests/js-native-api/test_array/test_array.c new file mode 100644 index 0000000..a451502 --- /dev/null +++ b/tests/js-native-api/test_array/test_array.c @@ -0,0 +1,188 @@ +#include +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value TestGetElement(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NODE_API_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an array as first argument."); + + napi_valuetype valuetype1; + NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NODE_API_ASSERT(env, valuetype1 == napi_number, + "Wrong type of arguments. Expects an integer as second argument."); + + napi_value array = args[0]; + int32_t index; + NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index)); + + NODE_API_ASSERT(env, index >= 0, "Invalid index. Expects a positive integer."); + + bool isarray; + NODE_API_CALL(env, napi_is_array(env, array, &isarray)); + + if (!isarray) { + return NULL; + } + + uint32_t length; + NODE_API_CALL(env, napi_get_array_length(env, array, &length)); + + NODE_API_ASSERT(env, ((uint32_t)index < length), "Index out of bounds!"); + + napi_value ret; + NODE_API_CALL(env, napi_get_element(env, array, index, &ret)); + + return ret; +} + +static napi_value TestHasElement(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NODE_API_ASSERT(env, argc >= 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NODE_API_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an array as first argument."); + + napi_valuetype valuetype1; + NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NODE_API_ASSERT(env, valuetype1 == napi_number, + "Wrong type of arguments. Expects an integer as second argument."); + + napi_value array = args[0]; + int32_t index; + NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index)); + + bool isarray; + NODE_API_CALL(env, napi_is_array(env, array, &isarray)); + + if (!isarray) { + return NULL; + } + + bool has_element; + NODE_API_CALL(env, napi_has_element(env, array, index, &has_element)); + + napi_value ret; + NODE_API_CALL(env, napi_get_boolean(env, has_element, &ret)); + + return ret; +} + +static napi_value TestDeleteElement(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + NODE_API_ASSERT(env, argc == 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype0)); + NODE_API_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an array as first argument."); + + napi_valuetype valuetype1; + NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1)); + NODE_API_ASSERT(env, valuetype1 == napi_number, + "Wrong type of arguments. Expects an integer as second argument."); + + napi_value array = args[0]; + int32_t index; + bool result; + napi_value ret; + + NODE_API_CALL(env, napi_get_value_int32(env, args[1], &index)); + NODE_API_CALL(env, napi_is_array(env, array, &result)); + + if (!result) { + return NULL; + } + + NODE_API_CALL(env, napi_delete_element(env, array, index, &result)); + NODE_API_CALL(env, napi_get_boolean(env, result, &ret)); + + return ret; +} + +static napi_value New(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_object, + "Wrong type of arguments. Expects an array as first argument."); + + napi_value ret; + NODE_API_CALL(env, napi_create_array(env, &ret)); + + uint32_t i, length; + NODE_API_CALL(env, napi_get_array_length(env, args[0], &length)); + + for (i = 0; i < length; i++) { + napi_value e; + NODE_API_CALL(env, napi_get_element(env, args[0], i, &e)); + NODE_API_CALL(env, napi_set_element(env, ret, i, e)); + } + + return ret; +} + +static napi_value NewWithLength(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 an integer the first argument."); + + int32_t array_length; + NODE_API_CALL(env, napi_get_value_int32(env, args[0], &array_length)); + + napi_value ret; + NODE_API_CALL(env, napi_create_array_with_length(env, array_length, &ret)); + + return ret; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("TestGetElement", TestGetElement), + DECLARE_NODE_API_PROPERTY("TestHasElement", TestHasElement), + DECLARE_NODE_API_PROPERTY("TestDeleteElement", TestDeleteElement), + DECLARE_NODE_API_PROPERTY("New", New), + DECLARE_NODE_API_PROPERTY("NewWithLength", NewWithLength), + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_bigint/CMakeLists.txt b/tests/js-native-api/test_bigint/CMakeLists.txt new file mode 100644 index 0000000..cb43b1f --- /dev/null +++ b/tests/js-native-api/test_bigint/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_bigint test_bigint.c) diff --git a/tests/js-native-api/test_bigint/test.js b/tests/js-native-api/test_bigint/test.js new file mode 100644 index 0000000..be779f6 --- /dev/null +++ b/tests/js-native-api/test_bigint/test.js @@ -0,0 +1,50 @@ +'use strict'; +const { + IsLossless, + TestInt64, + TestUint64, + TestWords, + CreateTooBigBigInt, + MakeBigIntWordsThrow, +} = loadAddon('test_bigint'); + +[ + 0n, + -0n, + 1n, + -1n, + 100n, + 2121n, + -1233n, + 986583n, + -976675n, + 98765432213456789876546896323445679887645323232436587988766545658n, + -4350987086545760976737453646576078997096876957864353245245769809n, +].forEach((num) => { + if (num > -(2n ** 63n) && num < 2n ** 63n) { + assert.strictEqual(TestInt64(num), num); + assert.strictEqual(IsLossless(num, true), true); + } else { + assert.strictEqual(IsLossless(num, true), false); + } + + if (num >= 0 && num < 2n ** 64n) { + assert.strictEqual(TestUint64(num), num); + assert.strictEqual(IsLossless(num, false), true); + } else { + assert.strictEqual(IsLossless(num, false), false); + } + + assert.strictEqual(num, TestWords(num)); +}); + +assert.throws(() => CreateTooBigBigInt(), { + name: 'Error', + message: 'Invalid argument', +}); + +// Test that we correctly forward exceptions from the engine. +assert.throws(() => MakeBigIntWordsThrow(), { + name: 'RangeError', + message: 'Maximum BigInt size exceeded', +}); diff --git a/tests/js-native-api/test_bigint/test_bigint.c b/tests/js-native-api/test_bigint/test_bigint.c new file mode 100644 index 0000000..2c61e0b --- /dev/null +++ b/tests/js-native-api/test_bigint/test_bigint.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value IsLossless(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + bool is_signed; + NODE_API_CALL(env, napi_get_value_bool(env, args[1], &is_signed)); + + bool lossless; + + if (is_signed) { + int64_t input; + NODE_API_CALL(env, napi_get_value_bigint_int64(env, args[0], &input, &lossless)); + } else { + uint64_t input; + NODE_API_CALL(env, napi_get_value_bigint_uint64(env, args[0], &input, &lossless)); + } + + napi_value output; + NODE_API_CALL(env, napi_get_boolean(env, lossless, &output)); + + return output; +} + +static napi_value TestInt64(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_bigint, + "Wrong type of arguments. Expects a bigint as first argument."); + + int64_t input; + bool lossless; + NODE_API_CALL(env, napi_get_value_bigint_int64(env, args[0], &input, &lossless)); + + napi_value output; + NODE_API_CALL(env, napi_create_bigint_int64(env, input, &output)); + + return output; +} + +static napi_value TestUint64(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_bigint, + "Wrong type of arguments. Expects a bigint as first argument."); + + uint64_t input; + bool lossless; + NODE_API_CALL(env, napi_get_value_bigint_uint64( + env, args[0], &input, &lossless)); + + napi_value output; + NODE_API_CALL(env, napi_create_bigint_uint64(env, input, &output)); + + return output; +} + +static napi_value TestWords(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_bigint, + "Wrong type of arguments. Expects a bigint as first argument."); + + size_t expected_word_count; + NODE_API_CALL(env, napi_get_value_bigint_words( + env, args[0], NULL, &expected_word_count, NULL)); + + int sign_bit; + size_t word_count = 10; + uint64_t words[10]; + + NODE_API_CALL(env, napi_get_value_bigint_words( + env, args[0], &sign_bit, &word_count, words)); + + NODE_API_ASSERT(env, word_count == expected_word_count, + "word counts do not match"); + + napi_value output; + NODE_API_CALL(env, napi_create_bigint_words( + env, sign_bit, word_count, words, &output)); + + return output; +} + +// throws RangeError +static napi_value CreateTooBigBigInt(napi_env env, napi_callback_info info) { + int sign_bit = 0; + size_t word_count = SIZE_MAX; + uint64_t words[10] = {0}; + + napi_value output; + + NODE_API_CALL(env, napi_create_bigint_words( + env, sign_bit, word_count, words, &output)); + + return output; +} + +// Test that we correctly forward exceptions from the engine. +static napi_value MakeBigIntWordsThrow(napi_env env, napi_callback_info info) { + uint64_t words[10] = {0}; + napi_value output; + + napi_status status = napi_create_bigint_words(env, + 0, + INT_MAX, + words, + &output); + if (status != napi_pending_exception) + napi_throw_error(env, NULL, "Expected status `napi_pending_exception`"); + + return NULL; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("IsLossless", IsLossless), + DECLARE_NODE_API_PROPERTY("TestInt64", TestInt64), + DECLARE_NODE_API_PROPERTY("TestUint64", TestUint64), + DECLARE_NODE_API_PROPERTY("TestWords", TestWords), + DECLARE_NODE_API_PROPERTY("CreateTooBigBigInt", CreateTooBigBigInt), + DECLARE_NODE_API_PROPERTY("MakeBigIntWordsThrow", MakeBigIntWordsThrow), + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_dataview/CMakeLists.txt b/tests/js-native-api/test_dataview/CMakeLists.txt new file mode 100644 index 0000000..4411f2e --- /dev/null +++ b/tests/js-native-api/test_dataview/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_dataview test_dataview.c) diff --git a/tests/js-native-api/test_dataview/test.js b/tests/js-native-api/test_dataview/test.js new file mode 100644 index 0000000..c267919 --- /dev/null +++ b/tests/js-native-api/test_dataview/test.js @@ -0,0 +1,21 @@ +'use strict'; +const test_dataview = loadAddon('test_dataview'); + +// Test for creating dataview with ArrayBuffer +{ + const buffer = new ArrayBuffer(128); + const template = Reflect.construct(DataView, [buffer]); + + const theDataview = test_dataview.CreateDataViewFromJSDataView(template); + assert(theDataview instanceof DataView, + `Expect ${theDataview} to be a DataView`); +} + +// Test for creating dataview with ArrayBuffer and invalid range +{ + const buffer = new ArrayBuffer(128); + assert.throws(() => { + test_dataview.CreateDataView(buffer, 10, 200); + }, RangeError); +} + diff --git a/tests/js-native-api/test_dataview/test_dataview.c b/tests/js-native-api/test_dataview/test_dataview.c new file mode 100644 index 0000000..ff774fa --- /dev/null +++ b/tests/js-native-api/test_dataview/test_dataview.c @@ -0,0 +1,104 @@ +#include +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value CreateDataView(napi_env env, napi_callback_info info) { + size_t argc = 3; + napi_value args [3]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NODE_API_ASSERT(env, argc == 3, "Wrong number of arguments"); + + napi_valuetype valuetype0; + napi_value arraybuffer = args[0]; + + NODE_API_CALL(env, napi_typeof(env, arraybuffer, &valuetype0)); + NODE_API_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects a ArrayBuffer as the first " + "argument."); + + bool is_arraybuffer; + NODE_API_CALL(env, napi_is_arraybuffer(env, arraybuffer, &is_arraybuffer)); + + NODE_API_ASSERT(env, + is_arraybuffer, + "Wrong type of arguments. Expects an ArrayBuffer as the " + "first argument."); + + napi_valuetype valuetype1; + NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1)); + + NODE_API_ASSERT(env, valuetype1 == napi_number, + "Wrong type of arguments. Expects a number as second argument."); + + size_t byte_offset = 0; + NODE_API_CALL(env, napi_get_value_uint32(env, args[1], (uint32_t*)(&byte_offset))); + + napi_valuetype valuetype2; + NODE_API_CALL(env, napi_typeof(env, args[2], &valuetype2)); + + NODE_API_ASSERT(env, valuetype2 == napi_number, + "Wrong type of arguments. Expects a number as third argument."); + + size_t length = 0; + NODE_API_CALL(env, napi_get_value_uint32(env, args[2], (uint32_t*)(&length))); + + napi_value output_dataview; + NODE_API_CALL(env, + napi_create_dataview(env, length, arraybuffer, + byte_offset, &output_dataview)); + + return output_dataview; +} + +static napi_value CreateDataViewFromJSDataView(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 valuetype; + napi_value input_dataview = args[0]; + + NODE_API_CALL(env, napi_typeof(env, input_dataview, &valuetype)); + NODE_API_ASSERT(env, valuetype == napi_object, + "Wrong type of arguments. Expects a DataView as the first " + "argument."); + + bool is_dataview; + NODE_API_CALL(env, napi_is_dataview(env, input_dataview, &is_dataview)); + NODE_API_ASSERT(env, is_dataview, + "Wrong type of arguments. Expects a DataView as the first " + "argument."); + size_t byte_offset = 0; + size_t length = 0; + napi_value buffer; + NODE_API_CALL(env, + napi_get_dataview_info(env, input_dataview, &length, NULL, + &buffer, &byte_offset)); + + napi_value output_dataview; + NODE_API_CALL(env, + napi_create_dataview(env, length, buffer, + byte_offset, &output_dataview)); + + + return output_dataview; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("CreateDataView", CreateDataView), + DECLARE_NODE_API_PROPERTY("CreateDataViewFromJSDataView", + CreateDataViewFromJSDataView) + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_date/CMakeLists.txt b/tests/js-native-api/test_date/CMakeLists.txt new file mode 100644 index 0000000..463a246 --- /dev/null +++ b/tests/js-native-api/test_date/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_date test_date.c) diff --git a/tests/js-native-api/test_date/test.js b/tests/js-native-api/test_date/test.js new file mode 100644 index 0000000..105c70a --- /dev/null +++ b/tests/js-native-api/test_date/test.js @@ -0,0 +1,15 @@ +'use strict'; +const test_date = loadAddon('test_date'); + +const dateTypeTestDate = test_date.createDate(1549183351); +assert.strictEqual(test_date.isDate(dateTypeTestDate), true); + +assert.strictEqual(test_date.isDate(new Date(1549183351)), true); + +assert.strictEqual(test_date.isDate(2.4), false); +assert.strictEqual(test_date.isDate('not a date'), false); +assert.strictEqual(test_date.isDate(undefined), false); +assert.strictEqual(test_date.isDate(null), false); +assert.strictEqual(test_date.isDate({}), false); + +assert.strictEqual(test_date.getDateValue(new Date(1549183351)), 1549183351); diff --git a/tests/js-native-api/test_date/test_date.c b/tests/js-native-api/test_date/test_date.c new file mode 100644 index 0000000..ef87d6d --- /dev/null +++ b/tests/js-native-api/test_date/test_date.c @@ -0,0 +1,64 @@ +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value createDate(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."); + + double time; + NODE_API_CALL(env, napi_get_value_double(env, args[0], &time)); + + napi_value date; + NODE_API_CALL(env, napi_create_date(env, time, &date)); + + return date; +} + +static napi_value isDate(napi_env env, napi_callback_info info) { + napi_value date, result; + size_t argc = 1; + bool is_date; + + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &date, NULL, NULL)); + NODE_API_CALL(env, napi_is_date(env, date, &is_date)); + NODE_API_CALL(env, napi_get_boolean(env, is_date, &result)); + + return result; +} + +static napi_value getDateValue(napi_env env, napi_callback_info info) { + napi_value date, result; + size_t argc = 1; + double value; + + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &date, NULL, NULL)); + NODE_API_CALL(env, napi_get_date_value(env, date, &value)); + NODE_API_CALL(env, napi_create_double(env, value, &result)); + + return result; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("createDate", createDate), + DECLARE_NODE_API_PROPERTY("isDate", isDate), + DECLARE_NODE_API_PROPERTY("getDateValue", getDateValue), + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_handle_scope/CMakeLists.txt b/tests/js-native-api/test_handle_scope/CMakeLists.txt new file mode 100644 index 0000000..228acb6 --- /dev/null +++ b/tests/js-native-api/test_handle_scope/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_handle_scope test_handle_scope.c) diff --git a/tests/js-native-api/test_handle_scope/test.js b/tests/js-native-api/test_handle_scope/test.js new file mode 100644 index 0000000..65d76a7 --- /dev/null +++ b/tests/js-native-api/test_handle_scope/test.js @@ -0,0 +1,14 @@ +'use strict'; +const testHandleScope = loadAddon('test_handle_scope'); + +testHandleScope.NewScope(); + +assert(testHandleScope.NewScopeEscape() instanceof Object); + +testHandleScope.NewScopeEscapeTwice(); + +assert.throws( + () => { + testHandleScope.NewScopeWithException(() => { throw new RangeError(); }); + }, + RangeError); diff --git a/tests/js-native-api/test_handle_scope/test_handle_scope.c b/tests/js-native-api/test_handle_scope/test_handle_scope.c new file mode 100644 index 0000000..832ce54 --- /dev/null +++ b/tests/js-native-api/test_handle_scope/test_handle_scope.c @@ -0,0 +1,86 @@ +#include +#include +#include "../common.h" +#include "../entry_point.h" + +// these tests validate the handle scope functions in the normal +// flow. Forcing gc behavior to fully validate they are doing +// the right right thing would be quite hard so we keep it +// simple for now. + +static napi_value NewScope(napi_env env, napi_callback_info info) { + napi_handle_scope scope; + napi_value output = NULL; + + NODE_API_CALL(env, napi_open_handle_scope(env, &scope)); + NODE_API_CALL(env, napi_create_object(env, &output)); + NODE_API_CALL(env, napi_close_handle_scope(env, scope)); + return NULL; +} + +static napi_value NewScopeEscape(napi_env env, napi_callback_info info) { + napi_escapable_handle_scope scope; + napi_value output = NULL; + napi_value escapee = NULL; + + NODE_API_CALL(env, napi_open_escapable_handle_scope(env, &scope)); + NODE_API_CALL(env, napi_create_object(env, &output)); + NODE_API_CALL(env, napi_escape_handle(env, scope, output, &escapee)); + NODE_API_CALL(env, napi_close_escapable_handle_scope(env, scope)); + return escapee; +} + +static napi_value NewScopeEscapeTwice(napi_env env, napi_callback_info info) { + napi_escapable_handle_scope scope; + napi_value output = NULL; + napi_value escapee = NULL; + napi_status status; + + NODE_API_CALL(env, napi_open_escapable_handle_scope(env, &scope)); + NODE_API_CALL(env, napi_create_object(env, &output)); + NODE_API_CALL(env, napi_escape_handle(env, scope, output, &escapee)); + status = napi_escape_handle(env, scope, output, &escapee); + NODE_API_ASSERT(env, status == napi_escape_called_twice, "Escaping twice fails"); + NODE_API_CALL(env, napi_close_escapable_handle_scope(env, scope)); + return NULL; +} + +static napi_value NewScopeWithException(napi_env env, napi_callback_info info) { + napi_handle_scope scope; + size_t argc; + napi_value exception_function; + napi_status status; + napi_value output = NULL; + + NODE_API_CALL(env, napi_open_handle_scope(env, &scope)); + NODE_API_CALL(env, napi_create_object(env, &output)); + + argc = 1; + NODE_API_CALL(env, napi_get_cb_info( + env, info, &argc, &exception_function, NULL, NULL)); + + status = napi_call_function( + env, output, exception_function, 0, NULL, NULL); + NODE_API_ASSERT(env, status == napi_pending_exception, + "Function should have thrown."); + + // Closing a handle scope should still work while an exception is pending. + NODE_API_CALL(env, napi_close_handle_scope(env, scope)); + return NULL; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor properties[] = { + DECLARE_NODE_API_PROPERTY("NewScope", NewScope), + DECLARE_NODE_API_PROPERTY("NewScopeEscape", NewScopeEscape), + DECLARE_NODE_API_PROPERTY("NewScopeEscapeTwice", NewScopeEscapeTwice), + DECLARE_NODE_API_PROPERTY("NewScopeWithException", NewScopeWithException), + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_instance_data/CMakeLists.txt b/tests/js-native-api/test_instance_data/CMakeLists.txt new file mode 100644 index 0000000..0077b9f --- /dev/null +++ b/tests/js-native-api/test_instance_data/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_instance_data test_instance_data.c) diff --git a/tests/js-native-api/test_instance_data/test.js b/tests/js-native-api/test_instance_data/test.js new file mode 100644 index 0000000..c0e6b9f --- /dev/null +++ b/tests/js-native-api/test_instance_data/test.js @@ -0,0 +1,5 @@ +'use strict'; +const test_instance_data = loadAddon('test_instance_data'); + +// Test that instance data can be accessed from a binding. +assert.strictEqual(test_instance_data.increment(), 42); diff --git a/tests/js-native-api/test_instance_data/test_instance_data.c b/tests/js-native-api/test_instance_data/test_instance_data.c new file mode 100644 index 0000000..5e33ddd --- /dev/null +++ b/tests/js-native-api/test_instance_data/test_instance_data.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include "../common.h" +#include "../entry_point.h" + +typedef struct { + size_t value; + bool print; + napi_ref js_cb_ref; +} AddonData; + +static napi_value Increment(napi_env env, napi_callback_info info) { + AddonData* data; + napi_value result; + + NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data)); + NODE_API_CALL(env, napi_create_uint32(env, ++data->value, &result)); + + return result; +} + +static void DeleteAddonData(napi_env env, void* raw_data, void* hint) { + AddonData* data = raw_data; + if (data->print) { + printf("deleting addon data\n"); + } + if (data->js_cb_ref != NULL) { + NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_cb_ref)); + } + free(data); +} + +static napi_value SetPrintOnDelete(napi_env env, napi_callback_info info) { + AddonData* data; + + NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data)); + data->print = true; + + return NULL; +} + +static void TestFinalizer(napi_env env, void* raw_data, void* hint) { + (void) raw_data; + (void) hint; + + AddonData* data; + NODE_API_CALL_RETURN_VOID(env, napi_get_instance_data(env, (void**)&data)); + napi_value js_cb, undefined; + NODE_API_CALL_RETURN_VOID(env, + napi_get_reference_value(env, data->js_cb_ref, &js_cb)); + NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined)); + NODE_API_CALL_RETURN_VOID(env, + napi_call_function(env, undefined, js_cb, 0, NULL, NULL)); + NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_cb_ref)); + data->js_cb_ref = NULL; +} + +static napi_value ObjectWithFinalizer(napi_env env, napi_callback_info info) { + AddonData* data; + napi_value result, js_cb; + size_t argc = 1; + + NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data)); + NODE_API_ASSERT(env, data->js_cb_ref == NULL, "reference must be NULL"); + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &js_cb, NULL, NULL)); + NODE_API_CALL(env, napi_create_object(env, &result)); + NODE_API_CALL(env, + napi_add_finalizer(env, result, NULL, TestFinalizer, NULL, NULL)); + NODE_API_CALL(env, napi_create_reference(env, js_cb, 1, &data->js_cb_ref)); + + return result; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + AddonData* data = malloc(sizeof(*data)); + data->value = 41; + data->print = false; + data->js_cb_ref = NULL; + + NODE_API_CALL(env, napi_set_instance_data(env, data, DeleteAddonData, NULL)); + + napi_property_descriptor props[] = { + DECLARE_NODE_API_PROPERTY("increment", Increment), + DECLARE_NODE_API_PROPERTY("setPrintOnDelete", SetPrintOnDelete), + DECLARE_NODE_API_PROPERTY("objectWithFinalizer", ObjectWithFinalizer), + }; + + NODE_API_CALL(env, + napi_define_properties( + env, exports, sizeof(props) / sizeof(*props), props)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_new_target/CMakeLists.txt b/tests/js-native-api/test_new_target/CMakeLists.txt new file mode 100644 index 0000000..897931d --- /dev/null +++ b/tests/js-native-api/test_new_target/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_new_target test_new_target.c) diff --git a/tests/js-native-api/test_new_target/test.js b/tests/js-native-api/test_new_target/test.js new file mode 100644 index 0000000..7d5a4ac --- /dev/null +++ b/tests/js-native-api/test_new_target/test.js @@ -0,0 +1,18 @@ +'use strict'; +const binding = loadAddon('test_new_target'); + +class Class extends binding.BaseClass { + constructor() { + super(); + this.method(); + } + method() { + this.ok = true; + } +} + +assert(new Class() instanceof binding.BaseClass); +assert(new Class().ok); +assert(binding.OrdinaryFunction()); +assert( + new binding.Constructor(binding.Constructor) instanceof binding.Constructor); diff --git a/tests/js-native-api/test_new_target/test_new_target.c b/tests/js-native-api/test_new_target/test_new_target.c new file mode 100644 index 0000000..4e2be97 --- /dev/null +++ b/tests/js-native-api/test_new_target/test_new_target.c @@ -0,0 +1,70 @@ +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value BaseClass(napi_env env, napi_callback_info info) { + napi_value newTargetArg; + NODE_API_CALL(env, napi_get_new_target(env, info, &newTargetArg)); + napi_value thisArg; + NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &thisArg, NULL)); + napi_value undefined; + NODE_API_CALL(env, napi_get_undefined(env, &undefined)); + + // this !== new.target since we are being invoked through super() + bool result; + NODE_API_CALL(env, napi_strict_equals(env, newTargetArg, thisArg, &result)); + NODE_API_ASSERT(env, !result, "this !== new.target"); + + // new.target !== undefined because we should be called as a new expression + NODE_API_ASSERT(env, newTargetArg != NULL, "newTargetArg != NULL"); + NODE_API_CALL(env, napi_strict_equals(env, newTargetArg, undefined, &result)); + NODE_API_ASSERT(env, !result, "new.target !== undefined"); + + return thisArg; +} + +static napi_value Constructor(napi_env env, napi_callback_info info) { + bool result; + napi_value newTargetArg; + NODE_API_CALL(env, napi_get_new_target(env, info, &newTargetArg)); + size_t argc = 1; + napi_value argv; + napi_value thisArg; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &argv, &thisArg, NULL)); + napi_value undefined; + NODE_API_CALL(env, napi_get_undefined(env, &undefined)); + + // new.target !== undefined because we should be called as a new expression + NODE_API_ASSERT(env, newTargetArg != NULL, "newTargetArg != NULL"); + NODE_API_CALL(env, napi_strict_equals(env, newTargetArg, undefined, &result)); + NODE_API_ASSERT(env, !result, "new.target !== undefined"); + + // arguments[0] should be Constructor itself (test harness passed it) + NODE_API_CALL(env, napi_strict_equals(env, newTargetArg, argv, &result)); + NODE_API_ASSERT(env, result, "new.target === Constructor"); + + return thisArg; +} + +static napi_value OrdinaryFunction(napi_env env, napi_callback_info info) { + napi_value newTargetArg; + NODE_API_CALL(env, napi_get_new_target(env, info, &newTargetArg)); + + NODE_API_ASSERT(env, newTargetArg == NULL, "newTargetArg == NULL"); + + napi_value _true; + NODE_API_CALL(env, napi_get_boolean(env, true, &_true)); + return _true; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + const napi_property_descriptor desc[] = { + DECLARE_NODE_API_PROPERTY("BaseClass", BaseClass), + DECLARE_NODE_API_PROPERTY("OrdinaryFunction", OrdinaryFunction), + DECLARE_NODE_API_PROPERTY("Constructor", Constructor) + }; + NODE_API_CALL(env, napi_define_properties(env, exports, 3, desc)); + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_number/CMakeLists.txt b/tests/js-native-api/test_number/CMakeLists.txt new file mode 100644 index 0000000..5e3bdbc --- /dev/null +++ b/tests/js-native-api/test_number/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_number test_number.c test_null.c) diff --git a/tests/js-native-api/test_number/test.js b/tests/js-native-api/test_number/test.js new file mode 100644 index 0000000..333c199 --- /dev/null +++ b/tests/js-native-api/test_number/test.js @@ -0,0 +1,131 @@ +'use strict'; +const test_number = loadAddon('test_number'); + +// Testing api calls for number +function testNumber(num) { + assert.strictEqual(test_number.Test(num), num); +} + +testNumber(0); +testNumber(-0); +testNumber(1); +testNumber(-1); +testNumber(100); +testNumber(2121); +testNumber(-1233); +testNumber(986583); +testNumber(-976675); + +/* eslint-disable no-loss-of-precision */ +testNumber( + 98765432213456789876546896323445679887645323232436587988766545658); +testNumber( + -4350987086545760976737453646576078997096876957864353245245769809); +/* eslint-enable no-loss-of-precision */ +testNumber(Number.MIN_SAFE_INTEGER); +testNumber(Number.MAX_SAFE_INTEGER); +testNumber(Number.MAX_SAFE_INTEGER + 10); + +testNumber(Number.MIN_VALUE); +testNumber(Number.MAX_VALUE); +testNumber(Number.MAX_VALUE + 10); + +testNumber(Number.POSITIVE_INFINITY); +testNumber(Number.NEGATIVE_INFINITY); +testNumber(Number.NaN); + +function testUint32(input, expected = input) { + assert.strictEqual(expected, test_number.TestUint32Truncation(input)); +} + +// Test zero +testUint32(0.0, 0); +testUint32(-0.0, 0); + +// Test overflow scenarios +testUint32(4294967295); +testUint32(4294967296, 0); +testUint32(4294967297, 1); +testUint32(17 * 4294967296 + 1, 1); +testUint32(-1, 0xffffffff); + +// Validate documented behavior when value is retrieved as 32-bit integer with +// `napi_get_value_int32` +function testInt32(input, expected = input) { + assert.strictEqual(expected, test_number.TestInt32Truncation(input)); +} + +// Test zero +testInt32(0.0, 0); +testInt32(-0.0, 0); + +// Test min/max int32 range +testInt32(-Math.pow(2, 31)); +testInt32(Math.pow(2, 31) - 1); + +// Test overflow scenarios +testInt32(4294967297, 1); +testInt32(4294967296, 0); +testInt32(4294967295, -1); +testInt32(4294967296 * 5 + 3, 3); + +// Test min/max safe integer range +testInt32(Number.MIN_SAFE_INTEGER, 1); +testInt32(Number.MAX_SAFE_INTEGER, -1); + +// Test within int64_t range (with precision loss) +testInt32(-Math.pow(2, 63) + (Math.pow(2, 9) + 1), 1024); +testInt32(Math.pow(2, 63) - (Math.pow(2, 9) + 1), -1024); + +// Test min/max double value +testInt32(-Number.MIN_VALUE, 0); +testInt32(Number.MIN_VALUE, 0); +testInt32(-Number.MAX_VALUE, 0); +testInt32(Number.MAX_VALUE, 0); + +// Test outside int64_t range +testInt32(-Math.pow(2, 63) + (Math.pow(2, 9)), 0); +testInt32(Math.pow(2, 63) - (Math.pow(2, 9)), 0); + +// Test non-finite numbers +testInt32(Number.POSITIVE_INFINITY, 0); +testInt32(Number.NEGATIVE_INFINITY, 0); +testInt32(Number.NaN, 0); + +// Validate documented behavior when value is retrieved as 64-bit integer with +// `napi_get_value_int64` +function testInt64(input, expected = input) { + assert.strictEqual(expected, test_number.TestInt64Truncation(input)); +} + +// Both V8 and ChakraCore return a sentinel value of `0x8000000000000000` when +// the conversion goes out of range, but V8 treats it as unsigned in some cases. +const RANGEERROR_POSITIVE = Math.pow(2, 63); +const RANGEERROR_NEGATIVE = -Math.pow(2, 63); + +// Test zero +testInt64(0.0, 0); +testInt64(-0.0, 0); + +// Test min/max safe integer range +testInt64(Number.MIN_SAFE_INTEGER); +testInt64(Number.MAX_SAFE_INTEGER); + +// Test within int64_t range (with precision loss) +testInt64(-Math.pow(2, 63) + (Math.pow(2, 9) + 1)); +testInt64(Math.pow(2, 63) - (Math.pow(2, 9) + 1)); + +// Test min/max double value +testInt64(-Number.MIN_VALUE, 0); +testInt64(Number.MIN_VALUE, 0); +testInt64(-Number.MAX_VALUE, RANGEERROR_NEGATIVE); +testInt64(Number.MAX_VALUE, RANGEERROR_POSITIVE); + +// Test outside int64_t range +testInt64(-Math.pow(2, 63) + (Math.pow(2, 9)), RANGEERROR_NEGATIVE); +testInt64(Math.pow(2, 63) - (Math.pow(2, 9)), RANGEERROR_POSITIVE); + +// Test non-finite numbers +testInt64(Number.POSITIVE_INFINITY, 0); +testInt64(Number.NEGATIVE_INFINITY, 0); +testInt64(Number.NaN, 0); diff --git a/tests/js-native-api/test_number/test_null.c b/tests/js-native-api/test_number/test_null.c new file mode 100644 index 0000000..37e8b24 --- /dev/null +++ b/tests/js-native-api/test_number/test_null.c @@ -0,0 +1,77 @@ +#include + +#include "../common.h" + +// Unifies the way the macros declare values. +typedef double double_t; + +#define BINDING_FOR_CREATE(initial_capital, lowercase) \ + static napi_value Create##initial_capital(napi_env env, \ + napi_callback_info info) { \ + napi_value return_value, call_result; \ + lowercase##_t value = 42; \ + NODE_API_CALL(env, napi_create_object(env, &return_value)); \ + add_returned_status(env, \ + "envIsNull", \ + return_value, \ + "Invalid argument", \ + napi_invalid_arg, \ + napi_create_##lowercase(NULL, value, &call_result)); \ + napi_create_##lowercase(env, value, NULL); \ + add_last_status(env, "resultIsNull", return_value); \ + return return_value; \ + } + +#define BINDING_FOR_GET_VALUE(initial_capital, lowercase) \ + static napi_value GetValue##initial_capital(napi_env env, \ + napi_callback_info info) { \ + napi_value return_value, call_result; \ + lowercase##_t value = 42; \ + NODE_API_CALL(env, napi_create_object(env, &return_value)); \ + NODE_API_CALL(env, napi_create_##lowercase(env, value, &call_result)); \ + add_returned_status( \ + env, \ + "envIsNull", \ + return_value, \ + "Invalid argument", \ + napi_invalid_arg, \ + napi_get_value_##lowercase(NULL, call_result, &value)); \ + napi_get_value_##lowercase(env, NULL, &value); \ + add_last_status(env, "valueIsNull", return_value); \ + napi_get_value_##lowercase(env, call_result, NULL); \ + add_last_status(env, "resultIsNull", return_value); \ + return return_value; \ + } + +BINDING_FOR_CREATE(Double, double) +BINDING_FOR_CREATE(Int32, int32) +BINDING_FOR_CREATE(Uint32, uint32) +BINDING_FOR_CREATE(Int64, int64) +BINDING_FOR_GET_VALUE(Double, double) +BINDING_FOR_GET_VALUE(Int32, int32) +BINDING_FOR_GET_VALUE(Uint32, uint32) +BINDING_FOR_GET_VALUE(Int64, int64) + +void init_test_null(napi_env env, napi_value exports) { + const napi_property_descriptor test_null_props[] = { + DECLARE_NODE_API_PROPERTY("createDouble", CreateDouble), + DECLARE_NODE_API_PROPERTY("createInt32", CreateInt32), + DECLARE_NODE_API_PROPERTY("createUint32", CreateUint32), + DECLARE_NODE_API_PROPERTY("createInt64", CreateInt64), + DECLARE_NODE_API_PROPERTY("getValueDouble", GetValueDouble), + DECLARE_NODE_API_PROPERTY("getValueInt32", GetValueInt32), + DECLARE_NODE_API_PROPERTY("getValueUint32", GetValueUint32), + DECLARE_NODE_API_PROPERTY("getValueInt64", GetValueInt64), + }; + napi_value test_null; + + NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &test_null)); + NODE_API_CALL_RETURN_VOID( + env, + napi_define_properties(env, + test_null, + sizeof(test_null_props) / sizeof(*test_null_props), + test_null_props)); + NODE_API_CALL_RETURN_VOID( + env, napi_set_named_property(env, exports, "testNull", test_null)); +} diff --git a/tests/js-native-api/test_number/test_null.h b/tests/js-native-api/test_number/test_null.h new file mode 100644 index 0000000..d5f6bf0 --- /dev/null +++ b/tests/js-native-api/test_number/test_null.h @@ -0,0 +1,8 @@ +#ifndef TEST_JS_NATIVE_API_TEST_NUMBER_TEST_NULL_H_ +#define TEST_JS_NATIVE_API_TEST_NUMBER_TEST_NULL_H_ + +#include + +void init_test_null(napi_env env, napi_value exports); + +#endif // TEST_JS_NATIVE_API_TEST_NUMBER_TEST_NULL_H_ diff --git a/tests/js-native-api/test_number/test_null.js b/tests/js-native-api/test_number/test_null.js new file mode 100644 index 0000000..0439c1b --- /dev/null +++ b/tests/js-native-api/test_number/test_null.js @@ -0,0 +1,16 @@ +'use strict'; +const { testNull } = loadAddon('test_number'); + +const expectedCreateResult = { + envIsNull: 'Invalid argument', + resultIsNull: 'Invalid argument', +}; +const expectedGetValueResult = { + envIsNull: 'Invalid argument', + resultIsNull: 'Invalid argument', + valueIsNull: 'Invalid argument', +}; +[ 'Double', 'Int32', 'Uint32', 'Int64' ].forEach((typeName) => { + assert.deepStrictEqual(testNull['create' + typeName](), expectedCreateResult); + assert.deepStrictEqual(testNull['getValue' + typeName](), expectedGetValueResult); +}); diff --git a/tests/js-native-api/test_number/test_number.c b/tests/js-native-api/test_number/test_number.c new file mode 100644 index 0000000..b816945 --- /dev/null +++ b/tests/js-native-api/test_number/test_number.c @@ -0,0 +1,110 @@ +#include +#include "../common.h" +#include "../entry_point.h" +#include "test_null.h" + +static napi_value Test(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."); + + double input; + NODE_API_CALL(env, napi_get_value_double(env, args[0], &input)); + + napi_value output; + NODE_API_CALL(env, napi_create_double(env, input, &output)); + + return output; +} + +static napi_value TestUint32Truncation(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."); + + uint32_t input; + NODE_API_CALL(env, napi_get_value_uint32(env, args[0], &input)); + + napi_value output; + NODE_API_CALL(env, napi_create_uint32(env, input, &output)); + + return output; +} + +static napi_value TestInt32Truncation(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 input; + NODE_API_CALL(env, napi_get_value_int32(env, args[0], &input)); + + napi_value output; + NODE_API_CALL(env, napi_create_int32(env, input, &output)); + + return output; +} + +static napi_value TestInt64Truncation(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."); + + int64_t input; + NODE_API_CALL(env, napi_get_value_int64(env, args[0], &input)); + + napi_value output; + NODE_API_CALL(env, napi_create_int64(env, input, &output)); + + return output; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("Test", Test), + DECLARE_NODE_API_PROPERTY("TestInt32Truncation", TestInt32Truncation), + DECLARE_NODE_API_PROPERTY("TestUint32Truncation", TestUint32Truncation), + DECLARE_NODE_API_PROPERTY("TestInt64Truncation", TestInt64Truncation), + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + init_test_null(env, exports); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_promise/CMakeLists.txt b/tests/js-native-api/test_promise/CMakeLists.txt new file mode 100644 index 0000000..041172e --- /dev/null +++ b/tests/js-native-api/test_promise/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_promise test_promise.c) diff --git a/tests/js-native-api/test_promise/test.js b/tests/js-native-api/test_promise/test.js new file mode 100644 index 0000000..850b96a --- /dev/null +++ b/tests/js-native-api/test_promise/test.js @@ -0,0 +1,72 @@ +'use strict'; +const test_promise = loadAddon('test_promise'); + +const tick = () => new Promise(resolve => setTimeout(resolve, 0)); + +// A resolution +{ + const expected_result = 42; + const promise = test_promise.createPromise(); + let resolved = false; + promise.then((result) => { + assert.strictEqual(result, expected_result); + resolved = true; + }); + test_promise.concludeCurrentPromise(expected_result, true); + await tick(); + assert(resolved, 'resolve callback was not called'); +} + +// A rejection +{ + const expected_result = 'It\'s not you, it\'s me.'; + const promise = test_promise.createPromise(); + let rejected = false; + let thenCalled = false; + promise.then( + () => { throw new Error('unexpected resolve'); }, + (result) => { + assert.strictEqual(result, expected_result); + rejected = true; + }, + ).then(() => { thenCalled = true; }); + test_promise.concludeCurrentPromise(expected_result, false); + await tick(); + assert(rejected, 'reject callback was not called'); + assert(thenCalled, 'then after catch was not called'); +} + +// Chaining +{ + const expected_result = 'chained answer'; + const promise = test_promise.createPromise(); + let resolved = false; + promise.then((result) => { + assert.strictEqual(result, expected_result); + resolved = true; + }); + test_promise.concludeCurrentPromise(Promise.resolve('chained answer'), true); + await tick(); + assert(resolved, 'chaining resolve callback was not called'); +} + +const promiseTypeTestPromise = test_promise.createPromise(); +assert.strictEqual(test_promise.isPromise(promiseTypeTestPromise), true); +test_promise.concludeCurrentPromise(undefined, true); + +const rejectPromise = Promise.reject(-1); +const expected_reason = -1; +assert.strictEqual(test_promise.isPromise(rejectPromise), true); +let caught = false; +rejectPromise.catch((reason) => { + assert.strictEqual(reason, expected_reason); + caught = true; +}); +await tick(); +assert(caught, 'catch was not called'); + +assert.strictEqual(test_promise.isPromise(2.4), false); +assert.strictEqual(test_promise.isPromise('I promise!'), false); +assert.strictEqual(test_promise.isPromise(undefined), false); +assert.strictEqual(test_promise.isPromise(null), false); +assert.strictEqual(test_promise.isPromise({}), false); diff --git a/tests/js-native-api/test_promise/test_promise.c b/tests/js-native-api/test_promise/test_promise.c new file mode 100644 index 0000000..eef4813 --- /dev/null +++ b/tests/js-native-api/test_promise/test_promise.c @@ -0,0 +1,64 @@ +#include +#include "../common.h" +#include "../entry_point.h" + +napi_deferred deferred = NULL; + +static napi_value createPromise(napi_env env, napi_callback_info info) { + napi_value promise; + + // We do not overwrite an existing deferred. + if (deferred != NULL) { + return NULL; + } + + NODE_API_CALL(env, napi_create_promise(env, &deferred, &promise)); + + return promise; +} + +static napi_value +concludeCurrentPromise(napi_env env, napi_callback_info info) { + napi_value argv[2]; + size_t argc = 2; + bool resolution; + + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); + NODE_API_CALL(env, napi_get_value_bool(env, argv[1], &resolution)); + if (resolution) { + NODE_API_CALL(env, napi_resolve_deferred(env, deferred, argv[0])); + } else { + NODE_API_CALL(env, napi_reject_deferred(env, deferred, argv[0])); + } + + deferred = NULL; + + return NULL; +} + +static napi_value isPromise(napi_env env, napi_callback_info info) { + napi_value promise, result; + size_t argc = 1; + bool is_promise; + + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &promise, NULL, NULL)); + NODE_API_CALL(env, napi_is_promise(env, promise, &is_promise)); + NODE_API_CALL(env, napi_get_boolean(env, is_promise, &result)); + + return result; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("createPromise", createPromise), + DECLARE_NODE_API_PROPERTY("concludeCurrentPromise", concludeCurrentPromise), + DECLARE_NODE_API_PROPERTY("isPromise", isPromise), + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_properties/CMakeLists.txt b/tests/js-native-api/test_properties/CMakeLists.txt new file mode 100644 index 0000000..df775d5 --- /dev/null +++ b/tests/js-native-api/test_properties/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_properties test_properties.c) diff --git a/tests/js-native-api/test_properties/test.js b/tests/js-native-api/test_properties/test.js new file mode 100644 index 0000000..3767c5d --- /dev/null +++ b/tests/js-native-api/test_properties/test.js @@ -0,0 +1,66 @@ +'use strict'; +const readonlyErrorRE = + /^TypeError: Cannot assign to read only property '.*' of object '#'$/; +const getterOnlyErrorRE = + /^TypeError: Cannot set property .* of # which has only a getter$/; + +// Testing api calls for defining properties +const test_object = loadAddon('test_properties'); + +assert.strictEqual(test_object.echo('hello'), 'hello'); + +test_object.readwriteValue = 1; +assert.strictEqual(test_object.readwriteValue, 1); +test_object.readwriteValue = 2; +assert.strictEqual(test_object.readwriteValue, 2); + +assert.throws(() => { test_object.readonlyValue = 3; }, readonlyErrorRE); + +assert.ok(test_object.hiddenValue); + +// Properties with napi_enumerable attribute should be enumerable. +const propertyNames = []; +for (const name in test_object) { + propertyNames.push(name); +} +assert(propertyNames.includes('echo')); +assert(propertyNames.includes('readwriteValue')); +assert(propertyNames.includes('readonlyValue')); +assert(!propertyNames.includes('hiddenValue')); +assert(propertyNames.includes('NameKeyValue')); +assert(!propertyNames.includes('readwriteAccessor1')); +assert(!propertyNames.includes('readwriteAccessor2')); +assert(!propertyNames.includes('readonlyAccessor1')); +assert(!propertyNames.includes('readonlyAccessor2')); + +// Validate properties created with symbol +const propertySymbols = Object.getOwnPropertySymbols(test_object); +assert.strictEqual(propertySymbols[0].toString(), 'Symbol(NameKeySymbol)'); +assert.strictEqual(propertySymbols[1].toString(), 'Symbol()'); +assert.strictEqual(propertySymbols[2], Symbol.for('NameKeySymbolFor')); + +// The napi_writable attribute should be ignored for accessors. +const readwriteAccessor1Descriptor = + Object.getOwnPropertyDescriptor(test_object, 'readwriteAccessor1'); +const readonlyAccessor1Descriptor = + Object.getOwnPropertyDescriptor(test_object, 'readonlyAccessor1'); +assert.ok(readwriteAccessor1Descriptor.get != null); +assert.ok(readwriteAccessor1Descriptor.set != null); +assert.strictEqual(readwriteAccessor1Descriptor.value, undefined); +assert.ok(readonlyAccessor1Descriptor.get != null); +assert.strictEqual(readonlyAccessor1Descriptor.set, undefined); +assert.strictEqual(readonlyAccessor1Descriptor.value, undefined); +test_object.readwriteAccessor1 = 1; +assert.strictEqual(test_object.readwriteAccessor1, 1); +assert.strictEqual(test_object.readonlyAccessor1, 1); +assert.throws(() => { test_object.readonlyAccessor1 = 3; }, getterOnlyErrorRE); +test_object.readwriteAccessor2 = 2; +assert.strictEqual(test_object.readwriteAccessor2, 2); +assert.strictEqual(test_object.readonlyAccessor2, 2); +assert.throws(() => { test_object.readonlyAccessor2 = 3; }, getterOnlyErrorRE); + +assert.strictEqual(test_object.hasNamedProperty(test_object, 'echo'), true); +assert.strictEqual(test_object.hasNamedProperty(test_object, 'hiddenValue'), + true); +assert.strictEqual(test_object.hasNamedProperty(test_object, 'doesnotexist'), + false); diff --git a/tests/js-native-api/test_properties/test_properties.c b/tests/js-native-api/test_properties/test_properties.c new file mode 100644 index 0000000..567dd8c --- /dev/null +++ b/tests/js-native-api/test_properties/test_properties.c @@ -0,0 +1,113 @@ +#define NAPI_VERSION 9 +#include +#include "../common.h" +#include "../entry_point.h" + +static double value_ = 1; + +static napi_value GetValue(napi_env env, napi_callback_info info) { + size_t argc = 0; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL)); + + NODE_API_ASSERT(env, argc == 0, "Wrong number of arguments"); + + napi_value number; + NODE_API_CALL(env, napi_create_double(env, value_, &number)); + + return number; +} + +static napi_value SetValue(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"); + + NODE_API_CALL(env, napi_get_value_double(env, args[0], &value_)); + + return NULL; +} + +static napi_value Echo(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"); + + return args[0]; +} + +static napi_value HasNamedProperty(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NODE_API_ASSERT(env, argc == 2, "Wrong number of arguments"); + + // Extract the name of the property to check + char buffer[128]; + size_t copied; + NODE_API_CALL(env, + napi_get_value_string_utf8(env, args[1], buffer, sizeof(buffer), &copied)); + + // do the check and create the boolean return value + bool value; + napi_value result; + NODE_API_CALL(env, napi_has_named_property(env, args[0], buffer, &value)); + NODE_API_CALL(env, napi_get_boolean(env, value, &result)); + + return result; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_value number; + NODE_API_CALL(env, napi_create_double(env, value_, &number)); + + napi_value name_value; + NODE_API_CALL(env, + napi_create_string_utf8( + env, "NameKeyValue", NAPI_AUTO_LENGTH, &name_value)); + + napi_value symbol_description; + napi_value name_symbol; + NODE_API_CALL(env, + napi_create_string_utf8( + env, "NameKeySymbol", NAPI_AUTO_LENGTH, &symbol_description)); + NODE_API_CALL(env, + napi_create_symbol(env, symbol_description, &name_symbol)); + + napi_value name_symbol_descriptionless; + NODE_API_CALL(env, + napi_create_symbol(env, NULL, &name_symbol_descriptionless)); + + napi_value name_symbol_for; + NODE_API_CALL(env, node_api_symbol_for(env, + "NameKeySymbolFor", + NAPI_AUTO_LENGTH, + &name_symbol_for)); + + napi_property_descriptor properties[] = { + { "echo", 0, Echo, 0, 0, 0, napi_enumerable, 0 }, + { "readwriteValue", 0, 0, 0, 0, number, napi_enumerable | napi_writable, 0 }, + { "readonlyValue", 0, 0, 0, 0, number, napi_enumerable, 0}, + { "hiddenValue", 0, 0, 0, 0, number, napi_default, 0}, + { NULL, name_value, 0, 0, 0, number, napi_enumerable, 0}, + { NULL, name_symbol, 0, 0, 0, number, napi_enumerable, 0}, + { NULL, name_symbol_descriptionless, 0, 0, 0, number, napi_enumerable, 0}, + { NULL, name_symbol_for, 0, 0, 0, number, napi_enumerable, 0}, + { "readwriteAccessor1", 0, 0, GetValue, SetValue, 0, napi_default, 0}, + { "readwriteAccessor2", 0, 0, GetValue, SetValue, 0, napi_writable, 0}, + { "readonlyAccessor1", 0, 0, GetValue, NULL, 0, napi_default, 0}, + { "readonlyAccessor2", 0, 0, GetValue, NULL, 0, napi_writable, 0}, + { "hasNamedProperty", 0, HasNamedProperty, 0, 0, 0, napi_default, 0 }, + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_reference_double_free/CMakeLists.txt b/tests/js-native-api/test_reference_double_free/CMakeLists.txt new file mode 100644 index 0000000..2d70b2b --- /dev/null +++ b/tests/js-native-api/test_reference_double_free/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_reference_double_free test_reference_double_free.c) diff --git a/tests/js-native-api/test_reference_double_free/test.js b/tests/js-native-api/test_reference_double_free/test.js new file mode 100644 index 0000000..13d411b --- /dev/null +++ b/tests/js-native-api/test_reference_double_free/test.js @@ -0,0 +1,9 @@ +'use strict'; + +// This test makes no assertions. It tests a fix without which it will crash +// with a double free. + +const addon = loadAddon('test_reference_double_free'); + +{ new addon.MyObject(true); } +{ new addon.MyObject(false); } diff --git a/tests/js-native-api/test_reference_double_free/test_reference_double_free.c b/tests/js-native-api/test_reference_double_free/test_reference_double_free.c new file mode 100644 index 0000000..0e0f91c --- /dev/null +++ b/tests/js-native-api/test_reference_double_free/test_reference_double_free.c @@ -0,0 +1,90 @@ +#include +#include +#include "../common.h" +#include "../entry_point.h" + +static size_t g_call_count = 0; + +static void Destructor(napi_env env, void* data, void* nothing) { + napi_ref* ref = data; + NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, *ref)); + free(ref); +} + +static void NoDeleteDestructor(napi_env env, void* data, void* hint) { + napi_ref* ref = data; + size_t* call_count = hint; + + // This destructor must be called exactly once. + if ((*call_count) > 0) abort(); + *call_count = ((*call_count) + 1); + free(ref); +} + +static napi_value New(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value js_this, js_delete; + bool delete; + napi_ref* ref = malloc(sizeof(*ref)); + + NODE_API_CALL(env, + napi_get_cb_info(env, info, &argc, &js_delete, &js_this, NULL)); + NODE_API_CALL(env, napi_get_value_bool(env, js_delete, &delete)); + + if (delete) { + NODE_API_CALL(env, + napi_wrap(env, js_this, ref, Destructor, NULL, ref)); + } else { + NODE_API_CALL(env, + napi_wrap(env, js_this, ref, NoDeleteDestructor, &g_call_count, ref)); + } + NODE_API_CALL(env, napi_reference_ref(env, *ref, NULL)); + + return js_this; +} + +static void NoopDeleter(napi_env env, void* data, void* hint) {} + +// Tests that calling napi_remove_wrap and napi_delete_reference consecutively +// doesn't crash the process. +// This is analogous to the test https://github.com/nodejs/node-addon-api/blob/main/test/objectwrap_constructor_exception.cc. +// In which the Napi::ObjectWrap<> is being destructed immediately after napi_wrap. +// As Napi::ObjectWrap<> is a subclass of Napi::Reference<>, napi_remove_wrap +// in the destructor of Napi::ObjectWrap<> is called before napi_delete_reference +// in the destructor of Napi::Reference<>. +static napi_value DeleteImmediately(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value js_obj; + napi_ref ref; + napi_valuetype type; + + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &js_obj, NULL, NULL)); + + NODE_API_CALL(env, napi_typeof(env, js_obj, &type)); + NODE_API_ASSERT(env, type == napi_object, "Expected object parameter"); + + NODE_API_CALL(env, napi_wrap(env, js_obj, NULL, NoopDeleter, NULL, &ref)); + NODE_API_CALL(env, napi_remove_wrap(env, js_obj, NULL)); + NODE_API_CALL(env, napi_delete_reference(env, ref)); + + return NULL; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_value myobj_ctor; + NODE_API_CALL(env, + napi_define_class( + env, "MyObject", NAPI_AUTO_LENGTH, New, NULL, 0, NULL, &myobj_ctor)); + NODE_API_CALL(env, + napi_set_named_property(env, exports, "MyObject", myobj_ctor)); + + napi_property_descriptor descriptors[] = { + DECLARE_NODE_API_PROPERTY("deleteImmediately", DeleteImmediately), + }; + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_reference_double_free/test_wrap.js b/tests/js-native-api/test_reference_double_free/test_wrap.js new file mode 100644 index 0000000..fece5de --- /dev/null +++ b/tests/js-native-api/test_reference_double_free/test_wrap.js @@ -0,0 +1,8 @@ +'use strict'; + +// This test makes no assertions. It tests that calling napi_remove_wrap and +// napi_delete_reference consecutively doesn't crash the process. + +const addon = loadAddon('test_reference_double_free'); + +addon.deleteImmediately({}); diff --git a/tests/js-native-api/test_symbol/CMakeLists.txt b/tests/js-native-api/test_symbol/CMakeLists.txt new file mode 100644 index 0000000..2bf0ef8 --- /dev/null +++ b/tests/js-native-api/test_symbol/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_symbol test_symbol.c) diff --git a/tests/js-native-api/test_symbol/test1.js b/tests/js-native-api/test_symbol/test1.js new file mode 100644 index 0000000..c6dd5e5 --- /dev/null +++ b/tests/js-native-api/test_symbol/test1.js @@ -0,0 +1,15 @@ +'use strict'; +const test_symbol = loadAddon('test_symbol'); + +const sym = test_symbol.New('test'); +assert.strictEqual(sym.toString(), 'Symbol(test)'); + +const myObj = {}; +const fooSym = test_symbol.New('foo'); +const otherSym = test_symbol.New('bar'); +myObj.foo = 'bar'; +myObj[fooSym] = 'baz'; +myObj[otherSym] = 'bing'; +assert.strictEqual(myObj.foo, 'bar'); +assert.strictEqual(myObj[fooSym], 'baz'); +assert.strictEqual(myObj[otherSym], 'bing'); diff --git a/tests/js-native-api/test_symbol/test2.js b/tests/js-native-api/test_symbol/test2.js new file mode 100644 index 0000000..208de02 --- /dev/null +++ b/tests/js-native-api/test_symbol/test2.js @@ -0,0 +1,13 @@ +'use strict'; +const test_symbol = loadAddon('test_symbol'); + +const fooSym = test_symbol.New('foo'); +assert.strictEqual(fooSym.toString(), 'Symbol(foo)'); + +const myObj = {}; +myObj.foo = 'bar'; +myObj[fooSym] = 'baz'; + +assert.deepStrictEqual(Object.keys(myObj), ['foo']); +assert.deepStrictEqual(Object.getOwnPropertyNames(myObj), ['foo']); +assert.deepStrictEqual(Object.getOwnPropertySymbols(myObj), [fooSym]); diff --git a/tests/js-native-api/test_symbol/test3.js b/tests/js-native-api/test_symbol/test3.js new file mode 100644 index 0000000..5d4e15b --- /dev/null +++ b/tests/js-native-api/test_symbol/test3.js @@ -0,0 +1,15 @@ +'use strict'; +const test_symbol = loadAddon('test_symbol'); + +assert.notStrictEqual(test_symbol.New(), test_symbol.New()); +assert.notStrictEqual(test_symbol.New('foo'), test_symbol.New('foo')); +assert.notStrictEqual(test_symbol.New('foo'), test_symbol.New('bar')); + +const foo1 = test_symbol.New('foo'); +const foo2 = test_symbol.New('foo'); +const object = { + [foo1]: 1, + [foo2]: 2, +}; +assert.strictEqual(object[foo1], 1); +assert.strictEqual(object[foo2], 2); diff --git a/tests/js-native-api/test_symbol/test_symbol.c b/tests/js-native-api/test_symbol/test_symbol.c new file mode 100644 index 0000000..b146582 --- /dev/null +++ b/tests/js-native-api/test_symbol/test_symbol.c @@ -0,0 +1,38 @@ +#include +#include "../common.h" +#include "../entry_point.h" + +static napi_value New(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)); + + napi_value description = NULL; + if (argc >= 1) { + napi_valuetype valuetype; + NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype)); + + NODE_API_ASSERT(env, valuetype == napi_string, + "Wrong type of arguments. Expects a string."); + + description = args[0]; + } + + napi_value symbol; + NODE_API_CALL(env, napi_create_symbol(env, description, &symbol)); + + return symbol; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor properties[] = { + DECLARE_NODE_API_PROPERTY("New", New), + }; + + NODE_API_CALL(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); + + return exports; +} +EXTERN_C_END From 2e9d32ff6bea9c6f517309cbe5fc68dae11f33cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sat, 28 Feb 2026 14:28:59 +0100 Subject: [PATCH 4/6] doc: update PORTING.md with ported status and experimental API notes Mark 17 easy js-native-api tests as Ported (test_dataview as Partial due to missing experimental SharedArrayBuffer APIs in node-api-headers). Add "Experimental Node-API Features" section documenting which tests depend on experimental APIs not yet available in node-api-headers, and the approach needed to support them. References #26. Co-Authored-By: Claude Opus 4.6 --- PORTING.md | 61 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/PORTING.md b/PORTING.md index 630c1f0..56697f7 100644 --- a/PORTING.md +++ b/PORTING.md @@ -22,36 +22,36 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h | Directory | Status | Difficulty | |---|---|---| | `2_function_arguments` | Ported | — | -| `3_callbacks` | Not ported | Easy | -| `4_object_factory` | Not ported | Easy | -| `5_function_factory` | Not ported | Easy | +| `3_callbacks` | Ported | Easy | +| `4_object_factory` | Ported | Easy | +| `5_function_factory` | Ported | Easy | | `6_object_wrap` | Not ported | Medium | -| `7_factory_wrap` | Not ported | Easy | -| `8_passing_wrapped` | Not ported | Easy | -| `test_array` | Not ported | Easy | -| `test_bigint` | Not ported | Easy | +| `7_factory_wrap` | Ported | Easy | +| `8_passing_wrapped` | Ported | Easy | +| `test_array` | Ported | Easy | +| `test_bigint` | Ported | Easy | | `test_cannot_run_js` | Not ported | Medium | | `test_constructor` | Not ported | Medium | | `test_conversions` | Not ported | Medium | -| `test_dataview` | Not ported | Easy | -| `test_date` | Not ported | Easy | +| `test_dataview` | Partial | Easy | +| `test_date` | Ported | Easy | | `test_error` | Not ported | Medium | | `test_exception` | Not ported | Medium | | `test_finalizer` | Not ported | Medium | | `test_function` | Not ported | Medium | | `test_general` | Not ported | Hard | -| `test_handle_scope` | Not ported | Easy | -| `test_instance_data` | Not ported | Easy | -| `test_new_target` | Not ported | Easy | -| `test_number` | Not ported | Easy | +| `test_handle_scope` | Ported | Easy | +| `test_instance_data` | Ported | Easy | +| `test_new_target` | Ported | Easy | +| `test_number` | Ported | Easy | | `test_object` | Not ported | Hard | -| `test_promise` | Not ported | Easy | -| `test_properties` | Not ported | Easy | +| `test_promise` | Ported | Easy | +| `test_properties` | Ported | Easy | | `test_reference` | Not ported | Medium | -| `test_reference_double_free` | Not ported | Easy | +| `test_reference_double_free` | Ported | Easy | | `test_sharedarraybuffer` | Not ported | Medium | | `test_string` | Not ported | Medium | -| `test_symbol` | Not ported | Easy | +| `test_symbol` | Ported | Easy | | `test_typedarray` | Not ported | Medium | ## Runtime-specific (`node-api`) @@ -87,6 +87,33 @@ Tests covering the runtime-specific part of Node-API, defined in `node_api.h`. | `test_worker_terminate` | Not ported | Hard | | `test_worker_terminate_finalization` | Not ported | Hard | +## Experimental Node-API Features + +Several tests in the upstream Node.js repository use experimental APIs that are guarded behind +`#ifdef NAPI_EXPERIMENTAL` in Node.js's `js_native_api.h`. The `node-api-headers` package (which +the CTS uses for compilation) does not currently include any of these experimental API declarations. + +When `NAPI_EXPERIMENTAL` is defined in Node.js, `NAPI_VERSION` is set to +`NAPI_VERSION_EXPERIMENTAL (2147483647)`. The `NAPI_MODULE` macro exports this version, and the +runtime uses it to decide whether to enable experimental behavior for that addon. + +| Feature macro | APIs | Used by | +|---|---|---| +| `NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER` | `node_api_is_sharedarraybuffer`, `node_api_create_sharedarraybuffer` | `test_dataview`, `test_sharedarraybuffer` | +| `NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES` | `node_api_create_object_with_properties` | `test_object` | +| `NODE_API_EXPERIMENTAL_HAS_SET_PROTOTYPE` | `node_api_set_prototype` | `test_general` | +| `NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER` | `node_api_post_finalizer` | `test_general`, `test_finalizer`, `6_object_wrap` | + +Tests that depend on these APIs are currently ported without the experimental test cases (marked +as "Partial" in the status column) or not ported at all. To fully support them, the CTS will need: + +1. A mechanism for compiling addons with experimental API access (either from updated + `node-api-headers`, CTS-provided forward declarations or copies of headers from the Node.js main repository) +2. A way for implementors to declare which experimental features their runtime supports +3. Conditional test execution that skips experimental assertions on runtimes that don't support them + +See [#26](https://github.com/nodejs/node-api-cts/issues/26) for the full design discussion. + ## Special Considerations ### `node_api_post_finalizer` (`6_object_wrap`, `test_finalizer`) From 9188967e9c7ff4a732656af0c5f93f22091d87b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sun, 1 Mar 2026 09:36:45 +0100 Subject: [PATCH 5/6] Cleaning up the porting docs --- PORTING.md | 148 +++++++++++++++++++++++++++-------------------------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/PORTING.md b/PORTING.md index 56697f7..f45c443 100644 --- a/PORTING.md +++ b/PORTING.md @@ -5,87 +5,88 @@ The source directories are [`test/js-native-api`](https://github.com/nodejs/node and [`test/node-api`](https://github.com/nodejs/node/tree/main/test/node-api) in the Node.js repository. Difficulty is assessed on two axes: + - **Size/complexity** — total lines of C/C++ and JS across all source files - **Runtime-API dependence** — pure `js_native_api.h` is cheapest; Node.js extensions and direct libuv calls require harness work or Node-only scoping -| Rating | Meaning | -|---|---| -| Easy | Small test, pure `js_native_api.h` or trivial runtime API, straightforward 1:1 port | -| Medium | Moderate size or uses a Node.js extension API that the harness will need to abstract | -| Hard | Large test and/or deep libuv/worker/SEA dependency; may need new harness primitives or Node-only scoping | +| Rating | Meaning | +| ------ | -------------------------------------------------------------------------------------------------------- | +| Easy | Small test, pure `js_native_api.h` or trivial runtime API, straightforward 1:1 port | +| Medium | Moderate size or uses a Node.js extension API that the harness will need to abstract | +| Hard | Large test and/or deep libuv/worker/SEA dependency; may need new harness primitives or Node-only scoping | ## Engine-specific (`js-native-api`) Tests covering the engine-specific part of Node-API, defined in `js_native_api.h`. -| Directory | Status | Difficulty | -|---|---|---| -| `2_function_arguments` | Ported | — | -| `3_callbacks` | Ported | Easy | -| `4_object_factory` | Ported | Easy | -| `5_function_factory` | Ported | Easy | -| `6_object_wrap` | Not ported | Medium | -| `7_factory_wrap` | Ported | Easy | -| `8_passing_wrapped` | Ported | Easy | -| `test_array` | Ported | Easy | -| `test_bigint` | Ported | Easy | -| `test_cannot_run_js` | Not ported | Medium | -| `test_constructor` | Not ported | Medium | -| `test_conversions` | Not ported | Medium | -| `test_dataview` | Partial | Easy | -| `test_date` | Ported | Easy | -| `test_error` | Not ported | Medium | -| `test_exception` | Not ported | Medium | -| `test_finalizer` | Not ported | Medium | -| `test_function` | Not ported | Medium | -| `test_general` | Not ported | Hard | -| `test_handle_scope` | Ported | Easy | -| `test_instance_data` | Ported | Easy | -| `test_new_target` | Ported | Easy | -| `test_number` | Ported | Easy | -| `test_object` | Not ported | Hard | -| `test_promise` | Ported | Easy | -| `test_properties` | Ported | Easy | -| `test_reference` | Not ported | Medium | -| `test_reference_double_free` | Ported | Easy | -| `test_sharedarraybuffer` | Not ported | Medium | -| `test_string` | Not ported | Medium | -| `test_symbol` | Ported | Easy | -| `test_typedarray` | Not ported | Medium | +| Directory | Status | Difficulty | +| ---------------------------- | ---------- | ---------- | +| `2_function_arguments` | Ported ✅ | — | +| `3_callbacks` | Ported ✅ | Easy | +| `4_object_factory` | Ported ✅ | Easy | +| `5_function_factory` | Ported ✅ | Easy | +| `6_object_wrap` | Not ported | Medium | +| `7_factory_wrap` | Ported ✅ | Easy | +| `8_passing_wrapped` | Ported ✅ | Easy | +| `test_array` | Ported ✅ | Easy | +| `test_bigint` | Ported ✅ | Easy | +| `test_cannot_run_js` | Not ported | Medium | +| `test_constructor` | Not ported | Medium | +| `test_conversions` | Not ported | Medium | +| `test_dataview` | Partial | Easy | +| `test_date` | Ported ✅ | Easy | +| `test_error` | Not ported | Medium | +| `test_exception` | Not ported | Medium | +| `test_finalizer` | Not ported | Medium | +| `test_function` | Not ported | Medium | +| `test_general` | Not ported | Hard | +| `test_handle_scope` | Ported ✅ | Easy | +| `test_instance_data` | Ported ✅ | Easy | +| `test_new_target` | Ported ✅ | Easy | +| `test_number` | Ported ✅ | Easy | +| `test_object` | Not ported | Hard | +| `test_promise` | Ported ✅ | Easy | +| `test_properties` | Ported ✅ | Easy | +| `test_reference` | Not ported | Medium | +| `test_reference_double_free` | Ported ✅ | Easy | +| `test_sharedarraybuffer` | Not ported | Medium | +| `test_string` | Not ported | Medium | +| `test_symbol` | Ported ✅ | Easy | +| `test_typedarray` | Not ported | Medium | ## Runtime-specific (`node-api`) Tests covering the runtime-specific part of Node-API, defined in `node_api.h`. -| Directory | Status | Difficulty | -|---|---|---| -| `1_hello_world` | Not ported | Easy | -| `test_async` | Not ported | Hard | -| `test_async_cleanup_hook` | Not ported | Hard | -| `test_async_context` | Not ported | Hard | -| `test_buffer` | Not ported | Medium | -| `test_callback_scope` | Not ported | Hard | -| `test_cleanup_hook` | Not ported | Medium | -| `test_env_teardown_gc` | Not ported | Easy | -| `test_exception` | Not ported | Easy | -| `test_fatal` | Not ported | Hard | -| `test_fatal_exception` | Not ported | Easy | -| `test_general` | Not ported | Medium | -| `test_init_order` | Not ported | Medium | -| `test_instance_data` | Not ported | Hard | -| `test_make_callback` | Not ported | Hard | -| `test_make_callback_recurse` | Not ported | Hard | -| `test_null_init` | Not ported | Medium | -| `test_reference_by_node_api_version` | Not ported | Medium | -| `test_sea_addon` | Not ported | Hard | -| `test_threadsafe_function` | Not ported | Hard | -| `test_threadsafe_function_shutdown` | Not ported | Hard | -| `test_uv_loop` | Not ported | Hard | -| `test_uv_threadpool_size` | Not ported | Hard | -| `test_worker_buffer_callback` | Not ported | Hard | -| `test_worker_terminate` | Not ported | Hard | -| `test_worker_terminate_finalization` | Not ported | Hard | +| Directory | Status | Difficulty | +| ------------------------------------ | ---------- | ---------- | +| `1_hello_world` | Not ported | Easy | +| `test_async` | Not ported | Hard | +| `test_async_cleanup_hook` | Not ported | Hard | +| `test_async_context` | Not ported | Hard | +| `test_buffer` | Not ported | Medium | +| `test_callback_scope` | Not ported | Hard | +| `test_cleanup_hook` | Not ported | Medium | +| `test_env_teardown_gc` | Not ported | Easy | +| `test_exception` | Not ported | Easy | +| `test_fatal` | Not ported | Hard | +| `test_fatal_exception` | Not ported | Easy | +| `test_general` | Not ported | Medium | +| `test_init_order` | Not ported | Medium | +| `test_instance_data` | Not ported | Hard | +| `test_make_callback` | Not ported | Hard | +| `test_make_callback_recurse` | Not ported | Hard | +| `test_null_init` | Not ported | Medium | +| `test_reference_by_node_api_version` | Not ported | Medium | +| `test_sea_addon` | Not ported | Hard | +| `test_threadsafe_function` | Not ported | Hard | +| `test_threadsafe_function_shutdown` | Not ported | Hard | +| `test_uv_loop` | Not ported | Hard | +| `test_uv_threadpool_size` | Not ported | Hard | +| `test_worker_buffer_callback` | Not ported | Hard | +| `test_worker_terminate` | Not ported | Hard | +| `test_worker_terminate_finalization` | Not ported | Hard | ## Experimental Node-API Features @@ -97,12 +98,12 @@ When `NAPI_EXPERIMENTAL` is defined in Node.js, `NAPI_VERSION` is set to `NAPI_VERSION_EXPERIMENTAL (2147483647)`. The `NAPI_MODULE` macro exports this version, and the runtime uses it to decide whether to enable experimental behavior for that addon. -| Feature macro | APIs | Used by | -|---|---|---| -| `NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER` | `node_api_is_sharedarraybuffer`, `node_api_create_sharedarraybuffer` | `test_dataview`, `test_sharedarraybuffer` | -| `NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES` | `node_api_create_object_with_properties` | `test_object` | -| `NODE_API_EXPERIMENTAL_HAS_SET_PROTOTYPE` | `node_api_set_prototype` | `test_general` | -| `NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER` | `node_api_post_finalizer` | `test_general`, `test_finalizer`, `6_object_wrap` | +| Feature macro | APIs | Used by | +| --------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------- | +| `NODE_API_EXPERIMENTAL_HAS_SHAREDARRAYBUFFER` | `node_api_is_sharedarraybuffer`, `node_api_create_sharedarraybuffer` | `test_dataview`, `test_sharedarraybuffer` | +| `NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES` | `node_api_create_object_with_properties` | `test_object` | +| `NODE_API_EXPERIMENTAL_HAS_SET_PROTOTYPE` | `node_api_set_prototype` | `test_general` | +| `NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER` | `node_api_post_finalizer` | `test_general`, `test_finalizer`, `6_object_wrap` | Tests that depend on these APIs are currently ported without the experimental test cases (marked as "Partial" in the status column) or not ported at all. To fully support them, the CTS will need: @@ -150,6 +151,7 @@ The following tests call into libuv directly — `napi_get_uv_event_loop`, `uv_t - `test_uv_loop`, `test_uv_threadpool_size` Porting options: + 1. **Node-only scope** — mark these tests as Node.js-only and skip on other runtimes. 2. **Harness abstraction** — introduce a minimal platform-agnostic threading/async API in the harness (e.g., `cts_thread_create`, `cts_async_schedule`) that implementors back with their From b726a6935874f3c12b40d2341af6990248e12674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Sun, 1 Mar 2026 11:48:57 +0100 Subject: [PATCH 6/6] feat: port test_constructor to CTS Exercises napi_define_class with method, data value, accessor, and static property descriptors. Includes null-argument tests for napi_define_class via test_null.c/test_null.js. Co-Authored-By: Claude Sonnet 4.6 --- PORTING.md | 2 +- .../test_constructor/CMakeLists.txt | 1 + tests/js-native-api/test_constructor/test.js | 58 +++++ tests/js-native-api/test_constructor/test2.js | 3 + .../test_constructor/test_constructor.c | 200 ++++++++++++++++++ .../test_constructor/test_null.c | 111 ++++++++++ .../test_constructor/test_null.h | 8 + .../test_constructor/test_null.js | 14 ++ 8 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 tests/js-native-api/test_constructor/CMakeLists.txt create mode 100644 tests/js-native-api/test_constructor/test.js create mode 100644 tests/js-native-api/test_constructor/test2.js create mode 100644 tests/js-native-api/test_constructor/test_constructor.c create mode 100644 tests/js-native-api/test_constructor/test_null.c create mode 100644 tests/js-native-api/test_constructor/test_null.h create mode 100644 tests/js-native-api/test_constructor/test_null.js diff --git a/PORTING.md b/PORTING.md index f45c443..f2616bc 100644 --- a/PORTING.md +++ b/PORTING.md @@ -32,7 +32,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h | `test_array` | Ported ✅ | Easy | | `test_bigint` | Ported ✅ | Easy | | `test_cannot_run_js` | Not ported | Medium | -| `test_constructor` | Not ported | Medium | +| `test_constructor` | Ported ✅ | Medium | | `test_conversions` | Not ported | Medium | | `test_dataview` | Partial | Easy | | `test_date` | Ported ✅ | Easy | diff --git a/tests/js-native-api/test_constructor/CMakeLists.txt b/tests/js-native-api/test_constructor/CMakeLists.txt new file mode 100644 index 0000000..dad41df --- /dev/null +++ b/tests/js-native-api/test_constructor/CMakeLists.txt @@ -0,0 +1 @@ +add_node_api_cts_addon(test_constructor test_constructor.c test_null.c) diff --git a/tests/js-native-api/test_constructor/test.js b/tests/js-native-api/test_constructor/test.js new file mode 100644 index 0000000..c0b214f --- /dev/null +++ b/tests/js-native-api/test_constructor/test.js @@ -0,0 +1,58 @@ +const getterOnlyErrorRE = + /^TypeError: Cannot set property .* of #<.*> which has only a getter$/; + +// Testing api calls for a constructor that defines properties +const TestConstructor = loadAddon('test_constructor'); +const test_object = new TestConstructor(); + +assert.strictEqual(test_object.echo('hello'), 'hello'); + +test_object.readwriteValue = 1; +assert.strictEqual(test_object.readwriteValue, 1); +test_object.readwriteValue = 2; +assert.strictEqual(test_object.readwriteValue, 2); + +assert.throws(() => { test_object.readonlyValue = 3; }, + /^TypeError: Cannot assign to read only property 'readonlyValue' of object '#'$/); + +assert.ok(test_object.hiddenValue); + +// Properties with napi_enumerable attribute should be enumerable. +const propertyNames = []; +for (const name in test_object) { + propertyNames.push(name); +} +assert.ok(propertyNames.includes('echo')); +assert.ok(propertyNames.includes('readwriteValue')); +assert.ok(propertyNames.includes('readonlyValue')); +assert.ok(!propertyNames.includes('hiddenValue')); +assert.ok(!propertyNames.includes('readwriteAccessor1')); +assert.ok(!propertyNames.includes('readwriteAccessor2')); +assert.ok(!propertyNames.includes('readonlyAccessor1')); +assert.ok(!propertyNames.includes('readonlyAccessor2')); + +// The napi_writable attribute should be ignored for accessors. +test_object.readwriteAccessor1 = 1; +assert.strictEqual(test_object.readwriteAccessor1, 1); +assert.strictEqual(test_object.readonlyAccessor1, 1); +assert.throws(() => { test_object.readonlyAccessor1 = 3; }, getterOnlyErrorRE); +test_object.readwriteAccessor2 = 2; +assert.strictEqual(test_object.readwriteAccessor2, 2); +assert.strictEqual(test_object.readonlyAccessor2, 2); +assert.throws(() => { test_object.readonlyAccessor2 = 3; }, getterOnlyErrorRE); + +// Validate that static properties are on the class as opposed +// to the instance +assert.strictEqual(TestConstructor.staticReadonlyAccessor1, 10); +assert.strictEqual(test_object.staticReadonlyAccessor1, undefined); + +// Verify that passing NULL to napi_define_class() results in the correct +// error. +assert.deepStrictEqual(TestConstructor.TestDefineClass(), { + envIsNull: 'Invalid argument', + nameIsNull: 'Invalid argument', + cbIsNull: 'Invalid argument', + cbDataIsNull: 'napi_ok', + propertiesIsNull: 'Invalid argument', + resultIsNull: 'Invalid argument', +}); diff --git a/tests/js-native-api/test_constructor/test2.js b/tests/js-native-api/test_constructor/test2.js new file mode 100644 index 0000000..c658b22 --- /dev/null +++ b/tests/js-native-api/test_constructor/test2.js @@ -0,0 +1,3 @@ +// Testing api calls for a constructor that defines properties +const TestConstructor = loadAddon('test_constructor').constructorName; +assert.strictEqual(TestConstructor.name, 'MyObject'); diff --git a/tests/js-native-api/test_constructor/test_constructor.c b/tests/js-native-api/test_constructor/test_constructor.c new file mode 100644 index 0000000..c706170 --- /dev/null +++ b/tests/js-native-api/test_constructor/test_constructor.c @@ -0,0 +1,200 @@ +#include +#include "../common.h" +#include "../entry_point.h" +#include "test_null.h" + +static double value_ = 1; +static double static_value_ = 10; + +static napi_value TestDefineClass(napi_env env, + napi_callback_info info) { + napi_status status; + napi_value result, return_value; + + napi_property_descriptor property_descriptor = { + "TestDefineClass", + NULL, + TestDefineClass, + NULL, + NULL, + NULL, + napi_enumerable | napi_static, + NULL}; + + NODE_API_CALL(env, napi_create_object(env, &return_value)); + + status = napi_define_class(NULL, + "TrackedFunction", + NAPI_AUTO_LENGTH, + TestDefineClass, + NULL, + 1, + &property_descriptor, + &result); + + add_returned_status(env, + "envIsNull", + return_value, + "Invalid argument", + napi_invalid_arg, + status); + + napi_define_class(env, + NULL, + NAPI_AUTO_LENGTH, + TestDefineClass, + NULL, + 1, + &property_descriptor, + &result); + + add_last_status(env, "nameIsNull", return_value); + + napi_define_class(env, + "TrackedFunction", + NAPI_AUTO_LENGTH, + NULL, + NULL, + 1, + &property_descriptor, + &result); + + add_last_status(env, "cbIsNull", return_value); + + napi_define_class(env, + "TrackedFunction", + NAPI_AUTO_LENGTH, + TestDefineClass, + NULL, + 1, + &property_descriptor, + &result); + + add_last_status(env, "cbDataIsNull", return_value); + + napi_define_class(env, + "TrackedFunction", + NAPI_AUTO_LENGTH, + TestDefineClass, + NULL, + 1, + NULL, + &result); + + add_last_status(env, "propertiesIsNull", return_value); + + + napi_define_class(env, + "TrackedFunction", + NAPI_AUTO_LENGTH, + TestDefineClass, + NULL, + 1, + &property_descriptor, + NULL); + + add_last_status(env, "resultIsNull", return_value); + + return return_value; +} + +static napi_value GetValue(napi_env env, napi_callback_info info) { + size_t argc = 0; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL)); + + NODE_API_ASSERT(env, argc == 0, "Wrong number of arguments"); + + napi_value number; + NODE_API_CALL(env, napi_create_double(env, value_, &number)); + + return number; +} + +static napi_value SetValue(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"); + + NODE_API_CALL(env, napi_get_value_double(env, args[0], &value_)); + + return NULL; +} + +static napi_value Echo(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"); + + return args[0]; +} + +static napi_value New(napi_env env, napi_callback_info info) { + napi_value _this; + NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL)); + + return _this; +} + +static napi_value GetStaticValue(napi_env env, napi_callback_info info) { + size_t argc = 0; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL)); + + NODE_API_ASSERT(env, argc == 0, "Wrong number of arguments"); + + napi_value number; + NODE_API_CALL(env, napi_create_double(env, static_value_, &number)); + + return number; +} + + +static napi_value NewExtra(napi_env env, napi_callback_info info) { + napi_value _this; + NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL)); + + return _this; +} + +EXTERN_C_START +napi_value Init(napi_env env, napi_value exports) { + napi_value number, cons; + NODE_API_CALL(env, napi_create_double(env, value_, &number)); + + NODE_API_CALL(env, napi_define_class( + env, "MyObject_Extra", 8, NewExtra, NULL, 0, NULL, &cons)); + + napi_property_descriptor properties[] = { + { "echo", NULL, Echo, NULL, NULL, NULL, napi_enumerable, NULL }, + { "readwriteValue", NULL, NULL, NULL, NULL, number, + napi_enumerable | napi_writable, NULL }, + { "readonlyValue", NULL, NULL, NULL, NULL, number, napi_enumerable, + NULL }, + { "hiddenValue", NULL, NULL, NULL, NULL, number, napi_default, NULL }, + { "readwriteAccessor1", NULL, NULL, GetValue, SetValue, NULL, napi_default, + NULL }, + { "readwriteAccessor2", NULL, NULL, GetValue, SetValue, NULL, + napi_writable, NULL }, + { "readonlyAccessor1", NULL, NULL, GetValue, NULL, NULL, napi_default, + NULL }, + { "readonlyAccessor2", NULL, NULL, GetValue, NULL, NULL, napi_writable, + NULL }, + { "staticReadonlyAccessor1", NULL, NULL, GetStaticValue, NULL, NULL, + napi_default | napi_static, NULL}, + { "constructorName", NULL, NULL, NULL, NULL, cons, + napi_enumerable | napi_static, NULL }, + { "TestDefineClass", NULL, TestDefineClass, NULL, NULL, NULL, + napi_enumerable | napi_static, NULL }, + }; + + NODE_API_CALL(env, napi_define_class(env, "MyObject", NAPI_AUTO_LENGTH, New, + NULL, sizeof(properties)/sizeof(*properties), properties, &cons)); + + init_test_null(env, cons); + + return cons; +} +EXTERN_C_END diff --git a/tests/js-native-api/test_constructor/test_null.c b/tests/js-native-api/test_constructor/test_null.c new file mode 100644 index 0000000..84f02f3 --- /dev/null +++ b/tests/js-native-api/test_constructor/test_null.c @@ -0,0 +1,111 @@ +#include + +#include "../common.h" +#include "test_null.h" + +static int some_data = 0; + +static napi_value TestConstructor(napi_env env, napi_callback_info info) { + return NULL; +} + +static napi_value TestDefineClass(napi_env env, napi_callback_info info) { + napi_value return_value, cons; + + const napi_property_descriptor prop = + DECLARE_NODE_API_PROPERTY("testConstructor", TestConstructor); + + NODE_API_CALL(env, napi_create_object(env, &return_value)); + add_returned_status(env, + "envIsNull", + return_value, + "Invalid argument", + napi_invalid_arg, + napi_define_class(NULL, + "TestClass", + NAPI_AUTO_LENGTH, + TestConstructor, + &some_data, + 1, + &prop, + &cons)); + + napi_define_class(env, + NULL, + NAPI_AUTO_LENGTH, + TestConstructor, + &some_data, + 1, + &prop, + &cons); + add_last_status(env, "nameIsNull", return_value); + + napi_define_class( + env, "TestClass", 0, TestConstructor, &some_data, 1, &prop, &cons); + add_last_status(env, "lengthIsZero", return_value); + + napi_define_class( + env, "TestClass", NAPI_AUTO_LENGTH, NULL, &some_data, 1, &prop, &cons); + add_last_status(env, "nativeSideIsNull", return_value); + + napi_define_class(env, + "TestClass", + NAPI_AUTO_LENGTH, + TestConstructor, + NULL, + 1, + &prop, + &cons); + add_last_status(env, "dataIsNull", return_value); + + napi_define_class(env, + "TestClass", + NAPI_AUTO_LENGTH, + TestConstructor, + &some_data, + 0, + &prop, + &cons); + add_last_status(env, "propsLengthIsZero", return_value); + + napi_define_class(env, + "TestClass", + NAPI_AUTO_LENGTH, + TestConstructor, + &some_data, + 1, + NULL, + &cons); + add_last_status(env, "propsIsNull", return_value); + + napi_define_class(env, + "TestClass", + NAPI_AUTO_LENGTH, + TestConstructor, + &some_data, + 1, + &prop, + NULL); + add_last_status(env, "resultIsNull", return_value); + + return return_value; +} + +void init_test_null(napi_env env, napi_value exports) { + napi_value test_null; + + const napi_property_descriptor test_null_props[] = { + DECLARE_NODE_API_PROPERTY("testDefineClass", TestDefineClass), + }; + + NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &test_null)); + NODE_API_CALL_RETURN_VOID( + env, + napi_define_properties(env, + test_null, + sizeof(test_null_props) / sizeof(*test_null_props), + test_null_props)); + + NODE_API_CALL_RETURN_VOID( + env, napi_set_named_property(env, exports, "testNull", test_null)); +} diff --git a/tests/js-native-api/test_constructor/test_null.h b/tests/js-native-api/test_constructor/test_null.h new file mode 100644 index 0000000..e10b708 --- /dev/null +++ b/tests/js-native-api/test_constructor/test_null.h @@ -0,0 +1,8 @@ +#ifndef TEST_JS_NATIVE_API_TEST_OBJECT_TEST_NULL_H_ +#define TEST_JS_NATIVE_API_TEST_OBJECT_TEST_NULL_H_ + +#include + +void init_test_null(napi_env env, napi_value exports); + +#endif // TEST_JS_NATIVE_API_TEST_OBJECT_TEST_NULL_H_ diff --git a/tests/js-native-api/test_constructor/test_null.js b/tests/js-native-api/test_constructor/test_null.js new file mode 100644 index 0000000..c61e0ec --- /dev/null +++ b/tests/js-native-api/test_constructor/test_null.js @@ -0,0 +1,14 @@ +// Test passing NULL to object-related Node-APIs. +const { testNull } = loadAddon('test_constructor'); +const expectedResult = { + envIsNull: 'Invalid argument', + nameIsNull: 'Invalid argument', + lengthIsZero: 'napi_ok', + nativeSideIsNull: 'Invalid argument', + dataIsNull: 'napi_ok', + propsLengthIsZero: 'napi_ok', + propsIsNull: 'Invalid argument', + resultIsNull: 'Invalid argument', +}; + +assert.deepStrictEqual(testNull.testDefineClass(), expectedResult);