Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
e72477c
JavaScriptCore wrapper previously passed nullptr to JSObjectCallAsFun…
matthargett Oct 12, 2025
b7551a7
Add the node-lite test suite Vlad added into hermes-windows. The JSC …
matthargett Oct 12, 2025
230134e
Android tests now pass. StdoutLogger was holding on to destroyed mute…
matthargett Oct 12, 2025
8c421ae
Run the macOS NodeApiTests under sanitizers, which found another bug …
matthargett Oct 13, 2025
c43e50d
Fix build errors. This deduplicates struct definitions that were inli…
matthargett Oct 15, 2025
a1f422d
always build the napi tests
matthargett Oct 16, 2025
79b9c4e
try and get address sanitizer and thread sanitizer to run on Android,…
matthargett Oct 16, 2025
45ca953
Add N-API version/conformance roadmap (folds in engine-compat baseline)
matthargett Jun 4, 2026
94c3795
Restore NodeApi tests build on current macOS toolchain
matthargett Jun 4, 2026
cdb2cde
Restore Android NodeApi test build (compiles/links/installs/runs on e…
matthargett Jun 4, 2026
3c12ac8
Fix Android NodeApi harness JNI crash; wire up SetNodeApiTestEnvironment
matthargett Jun 4, 2026
c910c8a
Android: make the NodeApi conformance tests actually execute on-device
matthargett Jun 4, 2026
02f6ffa
Android: enter the V8 context in jsr_open_napi_env_scope (fix napi_cr…
matthargett Jun 4, 2026
22128c4
Android/in-process node_lite: let ExitOnException propagate the fatal…
matthargett Jun 4, 2026
623909a
Android/in-process: make node_lite teardown destructors exception-safe
matthargett Jun 4, 2026
18ea297
Android/in-process: guard the fatal handler against throwing while un…
matthargett Jun 4, 2026
3cb88ad
Android/in-process: drop noexcept from throwing error-exit functions …
matthargett Jun 4, 2026
bd961f8
Android: skip in-process js-native-api addon tests pending shared-lib…
matthargett Jun 4, 2026
410fe78
docs(roadmap): document Android in-process addon-load constraint + sh…
matthargett Jun 4, 2026
813f169
Android: statically link conformance addons into the test binary (run…
matthargett Jun 5, 2026
97dd278
Android tests: pump native stdout/stderr to logcat
matthargett Jun 5, 2026
9329c83
docs(roadmap): Android v5 js-native-api now green via static linking …
matthargett Jun 5, 2026
719da1d
Android: drop the now-dead dynamic-.node build machinery (superseded …
matthargett Jun 5, 2026
97a9682
Android: remove vestigial V8Platform scaffolding from the env holder
matthargett Jun 5, 2026
760d8da
docs(roadmap): record node-api-cts FetchContent evaluation (task 6) —…
matthargett Jun 5, 2026
1f25d24
Android: dlopen conformance addons as dynamic .node backed by a share…
matthargett Jun 5, 2026
9e181dd
docs(roadmap): Android uses dynamic .node + shared libnapi.so (aligns…
matthargett Jun 5, 2026
fca9ff0
Android tests: use AndroidExtensions StdoutLogger for stdout->logcat
matthargett Jun 5, 2026
0fff7fc
docs(roadmap): stdout->logcat is via AndroidExtensions StdoutLogger, …
matthargett Jun 5, 2026
2801353
Sync napi shared-lib change with PR #183 (gate behind JSR_NAPI_SHARED…
matthargett Jun 5, 2026
ff1d595
Tests: enable the v5-clean reference double-free conformance test
matthargett Jun 5, 2026
ca49471
docs(roadmap): reference-test staging + GC-safety review (re hermes-w…
matthargett Jun 5, 2026
ee5edfa
Node-API: address #116 review (JSC call dispatch, status type, Window…
matthargett Jun 5, 2026
2dd8022
Keep JSI's napi.h self-contained (restores upstream's inlined type copy)
matthargett Sep 1, 2026
ebf1000
Tests/NodeApi: add missing <cstdint> in child_process.h (fixes Linux …
matthargett Jun 5, 2026
16d1442
Tests/node_lite: select the napi engine by compile-define, not platform
matthargett Jun 5, 2026
39f826e
Tests/NodeApi: link napi into addons on Windows/UWP so napi_* resolve…
matthargett Jun 5, 2026
de6ae6c
Tests: don't build the C-API conformance harness for the JSI backend
matthargett Sep 1, 2026
8bfbc40
Don't hard-fail the Linux JSC library probe (breaks the Hermes host b…
matthargett Sep 1, 2026
765643f
Tests/Android: build the on-device Node-API harness only for validate…
matthargett Sep 1, 2026
a101e3b
Don't default napi to a shared library for the Hermes engine on Android
matthargett Sep 1, 2026
07525fc
Node-API: raise default NAPI_VERSION 5 -> 7
matthargett Jun 5, 2026
cca496d
JSC: implement napi_set/get_instance_data (N-API v6)
matthargett Jun 5, 2026
50bbb4a
JSC: implement N-API v6 BigInt + v7 ArrayBuffer detach
matthargett Jun 5, 2026
128c8d0
tests: note detach/property_names conformance tests are v9-gated (nod…
matthargett Jun 5, 2026
995ddf2
JSC: detect BigInt in napi_typeof on jsc-android (cached typeof predi…
matthargett Jun 5, 2026
259c591
Chakra/JSI: N-API v6/v7 surface for the cross-engine version bump
matthargett Jun 5, 2026
1af114d
Tests/Android: run test_bigint on V8 only (jsc-android ~2020 BigInt s…
matthargett Jun 5, 2026
0b07668
JSC: feature-detect BigInt; throw ENOTSUP where the engine lacks it (…
matthargett Jun 5, 2026
807241a
Tests/NodeApi: add literal-free test_bigint_unsupported fallback; gat…
matthargett Jun 5, 2026
62739d5
Tests/Android: surface in-process node_lite failure detail to logcat
matthargett Jun 5, 2026
4fbae5d
Tests/NodeApi: gate the v6/v7 conformance addons by an engine allow-list
matthargett Sep 1, 2026
4db0d03
Tests/Android: apply the same v6/v7 engine allow-list to the on-devic…
matthargett Sep 1, 2026
94cad11
Tests/Android: keep the v6/v7 test list inside the harness guard
matthargett Sep 1, 2026
d69268d
Tests/Android: fix the dead guard that silently disabled the on-devic…
matthargett Sep 1, 2026
9924974
Core/Node-API: pin jsc-android to r294992, the newest build, which ha…
matthargett Sep 1, 2026
9a9e06c
JSC: resolve BigInt and ArrayBuffer detach through intrinsics capture…
matthargett Sep 1, 2026
f268254
Core/Node-API: bump the node-addon-api header to NAPI_VERSION 7
matthargett Sep 1, 2026
2503f2c
JSC: keep the napi_typeof BigInt fallback off the object path
matthargett Sep 1, 2026
4a3491b
Tests: cover napi_detach_arraybuffer, the API that defines N-API v7
matthargett Sep 1, 2026
a041fad
V8: napi_is_detached_arraybuffer reported every ArrayBuffer as detached
matthargett Sep 1, 2026
9de995b
Tests/NodeApi: drain the child's pipes concurrently in SpawnSync inst…
matthargett Sep 13, 2026
3b38a98
Chakra: implement the N-API v7 ArrayBuffer detach pair
matthargett Sep 1, 2026
631b745
Merge upstream main (62818ee) into napi-tests
matthargett Sep 25, 2026
5cd8bfa
Merge napi-tests (631b745: upstream main 62818ee) into napi-v7
matthargett Sep 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ endif()
FetchContent_MakeAvailable_With_Message(arcana.cpp)
set_property(TARGET arcana PROPERTY FOLDER Dependencies)

if(ANDROID)
FetchContent_GetProperties(AndroidExtensions)
if(NOT AndroidExtensions_POPULATED)
FetchContent_Populate(AndroidExtensions)
FetchContent_GetProperties(AndroidExtensions)
add_subdirectory(${androidextensions_SOURCE_DIR} ${androidextensions_BINARY_DIR})
else()
add_subdirectory(${androidextensions_SOURCE_DIR} ${androidextensions_BINARY_DIR})
endif()
endif()

if(JSRUNTIMEHOST_POLYFILL_XMLHTTPREQUEST OR JSRUNTIMEHOST_POLYFILL_FETCH)
FetchContent_MakeAvailable_With_Message(UrlLib)
set_property(TARGET UrlLib PROPERTY FOLDER Dependencies)
Expand Down
31 changes: 29 additions & 2 deletions Core/Node-API/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ if(NAPI_BUILD_ABI)
if(ANDROID)
set(V8_PACKAGE_NAME "jsc-android")
set(JSC_ANDROID_DIR "${CMAKE_CURRENT_BINARY_DIR}/${V8_PACKAGE_NAME}")
napi_install_android_package(jsc "dist/org/webkit/android-jsc" ${JSC_ANDROID_DIR})
# r294992 is the newest jsc-android build (dist-tag `next`; `latest` is still the 2019
# r250231). It is the first with BigInt enabled by default -- r250231 gates BigInt behind
# the `useBigInt` runtime option, off, so its parser rejects `0n`. r294992 publishes only
# the `-intl` variant, and statically links libc++ (no libc++_shared.so dependency).
napi_install_android_package(jsc "dist/org/webkit/android-jsc-intl" ${JSC_ANDROID_DIR})

# Add `JavaScriptCore` prefix to the include path
file(RENAME "${JSC_ANDROID_DIR}/include" "${JSC_ANDROID_DIR}/JavaScriptCore")
Expand Down Expand Up @@ -261,7 +265,24 @@ Make sure Hermes was fetched at the top-level CMakeLists.txt and NAPI_JAVASCRIPT
message(STATUS "Selected ${NAPI_JAVASCRIPT_ENGINE}")
endif()

add_library(napi ${SOURCES})
# On Android, native addons are dlopen'd as standalone .node modules and resolve their napi_* imports
# from a shared napi at load time -- bionic will not surface a statically-linked host's napi to a
# dlopen'd module, so the host and every addon must share a single libnapi.so. Default napi to a
# shared library on Android so that model works out of the box; an integrator who wants a static napi
# (e.g. for size/packaging) can override with -DJSR_NAPI_SHARED=OFF. The option defaults OFF on other
# platforms, where napi keeps following the project's default library type (i.e. honors
# BUILD_SHARED_LIBS).
set(JSR_NAPI_SHARED_DEFAULT OFF)
if(ANDROID AND NOT NAPI_JAVASCRIPT_ENGINE STREQUAL "Hermes")
set(JSR_NAPI_SHARED_DEFAULT ON)
endif()
option(JSR_NAPI_SHARED "Build napi as a shared library (libnapi.so)" ${JSR_NAPI_SHARED_DEFAULT})

if(JSR_NAPI_SHARED)
add_library(napi SHARED ${SOURCES})
else()
add_library(napi ${SOURCES})
endif()

target_include_directories(napi ${INCLUDE_DIRECTORIES})
target_link_libraries(napi ${LINK_LIBRARIES})
Expand All @@ -277,5 +298,11 @@ if(NAPI_JAVASCRIPT_ENGINE STREQUAL "Hermes")
NAPI_HERMES_MAX_HEAP_SIZE_MB=${NAPI_HERMES_MAX_HEAP_SIZE_MB})
endif()

# Expose the selected engine as a compile definition so engine-agnostic consumers (the node_lite test
# harness) can branch on engine capability instead of guessing from the platform
# (__APPLE__ == JSC / __ANDROID__ == V8 breaks Android-JSC, Linux-V8, Windows-Chakra, ...).
string(TOUPPER "${NAPI_JAVASCRIPT_ENGINE}" NAPI_ENGINE_UPPER)
target_compile_definitions(napi PUBLIC JSR_NAPI_ENGINE_${NAPI_ENGINE_UPPER})

set_property(TARGET napi PROPERTY FOLDER Dependencies)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES})
2 changes: 1 addition & 1 deletion Core/Node-API/Include/Shared/napi/js_native_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// [BABYLON-NATIVE-ADDITION]
#ifndef NAPI_VERSION
#define NAPI_VERSION 5
#define NAPI_VERSION 7
#endif

// This file needs to be compatible with C compilers.
Expand Down
2 changes: 1 addition & 1 deletion Core/Node-API/Include/Shared/napi/js_native_api_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// [BABYLON-NATIVE-ADDITION]
#ifndef NAPI_VERSION
#define NAPI_VERSION 5
#define NAPI_VERSION 7
#endif

// This file needs to be compatible with C compilers.
Expand Down
7 changes: 6 additions & 1 deletion Core/Node-API/Include/Shared/napi/napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
#ifndef NODE_ADDON_API_DISABLE_NODE_SPECIFIC
#define NODE_ADDON_API_DISABLE_NODE_SPECIFIC
#endif
// [BABYLON-NATIVE-ADDITION] Keep this in step with js_native_api.h / js_native_api_types.h. The C
// headers already default to 7, but this one is reached first by every consumer that includes
// <napi/napi.h> (all of Babylon Native, and every node-addon-api addon), and #ifndef made it win --
// so Napi::BigInt, Value::IsBigInt, GetInstanceData<T> and Napi::Addon stayed compiled out even
// though the C entry points behind them were implemented.
#ifndef NAPI_VERSION
#define NAPI_VERSION 5
#define NAPI_VERSION 7
#endif
#ifndef NAPI_HAS_THREADS
#define NAPI_HAS_THREADS 0
Expand Down
113 changes: 113 additions & 0 deletions Core/Node-API/Source/js_native_api_chakra.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2486,6 +2486,119 @@ napi_status napi_run_script(napi_env env,
return napi_ok;
}

// === N-API v6 / v7 ===
//
// napi_set_instance_data / napi_get_instance_data (v6): per-env data slot, finalized at env teardown
// by ~napi_env__ (see js_native_api_chakra.h).
napi_status napi_set_instance_data(napi_env env,
void* data,
napi_finalize finalize_cb,
void* finalize_hint) {
CHECK_ENV(env);
env->instance_data = data;
env->instance_data_finalize_cb = finalize_cb;
env->instance_data_finalize_hint = finalize_hint;
return napi_ok;
}

napi_status napi_get_instance_data(napi_env env, void** data) {
CHECK_ENV(env);
CHECK_ARG(env, data);
*data = env->instance_data;
return napi_ok;
}

// N-API v7 ArrayBuffer detach. Win10's OS Chakra exposes no way to detach an ArrayBuffer: the
// runtime has no detach entry point, and its JavaScript engine predates ES2024
// ArrayBuffer.prototype.transfer(). As with BigInt below, report that honestly rather than failing
// with a bare status an addon cannot tell from a real error.
napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer) {
CHECK_ENV(env);
CHECK_ARG(env, arraybuffer);
CHECK_NAPI(napi_throw_error(
env, "ENOTSUP",
"ArrayBuffer detach is not supported by the underlying JavaScript engine (Chakra)."));
return napi_set_last_error(env, napi_pending_exception);
}

napi_status napi_is_detached_arraybuffer(napi_env env,
napi_value arraybuffer,
bool* result) {
CHECK_ENV(env);
CHECK_ARG(env, arraybuffer);
CHECK_ARG(env, result);

// Nothing can detach a buffer on this engine (see above), so an ArrayBuffer that still reports
// storage is live. Non-ArrayBuffers are not detached either, matching Node's contract.
JsValueType valueType;
CHECK_JSRT(env, JsGetValueType(reinterpret_cast<JsValueRef>(arraybuffer), &valueType));
if (valueType != JsArrayBuffer) {
*result = false;
return napi_ok;
}

BYTE* storageData;
unsigned int storageLength;
CHECK_JSRT(env, JsGetArrayBufferStorage(
reinterpret_cast<JsValueRef>(arraybuffer),
&storageData,
&storageLength));

*result = (storageData == nullptr);
return napi_ok;
}

// BigInt (v6): the Win10 OS edge-mode Chakra (jsrt) predates BigInt and exposes no JsBigInt* API, so
// there is no value-preserving fallback. Per the Node-API feature-detection-by-exception pattern, throw
// a JS-catchable error tagged "ENOTSUP" (so JS land can detect + polyfill) and return a pending
// exception rather than silently failing. (ChakraCore added BigInt behind a flag, but the OS Chakra
// this backend targets did not ship it.)
static napi_status napi_bigint_not_supported(napi_env env) {
CHECK_ENV(env);
CHECK_NAPI(napi_throw_error(
env, "ENOTSUP",
"BigInt is not supported by the underlying JavaScript engine (Chakra)."));
return napi_set_last_error(env, napi_pending_exception);
}

napi_status napi_create_bigint_int64(napi_env env, int64_t value, napi_value* result) {
return napi_bigint_not_supported(env);
}

napi_status napi_create_bigint_uint64(napi_env env, uint64_t value, napi_value* result) {
return napi_bigint_not_supported(env);
}

napi_status napi_create_bigint_words(napi_env env,
int sign_bit,
size_t word_count,
const uint64_t* words,
napi_value* result) {
return napi_bigint_not_supported(env);
}

napi_status napi_get_value_bigint_int64(napi_env env,
napi_value value,
int64_t* result,
bool* lossless) {
return napi_bigint_not_supported(env);
}

napi_status napi_get_value_bigint_uint64(napi_env env,
napi_value value,
uint64_t* result,
bool* lossless) {
return napi_bigint_not_supported(env);
}

napi_status napi_get_value_bigint_words(napi_env env,
napi_value value,
int* sign_bit,
size_t* word_count,
uint64_t* words) {
return napi_bigint_not_supported(env);
}

napi_status napi_add_finalizer(napi_env env,
napi_value js_object,
void* native_object,
Expand Down
12 changes: 12 additions & 0 deletions Core/Node-API/Source/js_native_api_chakra.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,19 @@ struct napi_env__ {
size_t next_escapable_scope_token = 0;
std::map<size_t, bool> open_escapable_scopes;

// napi_set_instance_data / napi_get_instance_data (N-API v6).
void* instance_data = nullptr;
napi_finalize instance_data_finalize_cb = nullptr;
void* instance_data_finalize_hint = nullptr;

const std::thread::id thread_id{std::this_thread::get_id()};

~napi_env__() {
// Run the instance-data finalizer at env teardown (env_chakra.cc deletes the env), matching V8/JSC.
if (instance_data_finalize_cb != nullptr) {
instance_data_finalize_cb(this, instance_data, instance_data_finalize_hint);
}
}
};

#define RETURN_STATUS_IF_FALSE(env, condition, status) \
Expand Down
Loading