Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
25dcff7
JavaScriptCore wrapper previously passed nullptr to JSObjectCallAsFun…
matthargett Oct 12, 2025
efcb060
Add the node-lite test suite Vlad added into hermes-windows. The JSC …
matthargett Oct 12, 2025
8dbd11e
Android tests now pass. StdoutLogger was holding on to destroyed mute…
matthargett Oct 12, 2025
dfaccbe
Run the macOS NodeApiTests under sanitizers, which found another bug …
matthargett Oct 13, 2025
ef2949f
Fix build errors. This deduplicates struct definitions that were inli…
matthargett Oct 15, 2025
b725184
always build the napi tests
matthargett Oct 16, 2025
c68b5e9
try and get address sanitizer and thread sanitizer to run on Android,…
matthargett Oct 16, 2025
1952f7b
Add N-API version/conformance roadmap (folds in engine-compat baseline)
matthargett Jun 4, 2026
cc265c3
Restore NodeApi tests build on current macOS toolchain
matthargett Jun 4, 2026
4eb18cc
Restore Android NodeApi test build (compiles/links/installs/runs on e…
matthargett Jun 4, 2026
ca21767
Fix Android NodeApi harness JNI crash; wire up SetNodeApiTestEnvironment
matthargett Jun 4, 2026
c486dc4
Android: make the NodeApi conformance tests actually execute on-device
matthargett Jun 4, 2026
ab74f9e
Android: enter the V8 context in jsr_open_napi_env_scope (fix napi_cr…
matthargett Jun 4, 2026
c9ff696
Android/in-process node_lite: let ExitOnException propagate the fatal…
matthargett Jun 4, 2026
9e0a62c
Android/in-process: make node_lite teardown destructors exception-safe
matthargett Jun 4, 2026
09e9678
Android/in-process: guard the fatal handler against throwing while un…
matthargett Jun 4, 2026
4fea275
Android/in-process: drop noexcept from throwing error-exit functions …
matthargett Jun 4, 2026
0a78615
Android: skip in-process js-native-api addon tests pending shared-lib…
matthargett Jun 4, 2026
a8282b7
docs(roadmap): document Android in-process addon-load constraint + sh…
matthargett Jun 4, 2026
e0484e1
Android: statically link conformance addons into the test binary (run…
matthargett Jun 5, 2026
aa1daff
Android tests: pump native stdout/stderr to logcat
matthargett Jun 5, 2026
9013f87
docs(roadmap): Android v5 js-native-api now green via static linking …
matthargett Jun 5, 2026
7ca16d9
Android: drop the now-dead dynamic-.node build machinery (superseded …
matthargett Jun 5, 2026
8949da4
Android: remove vestigial V8Platform scaffolding from the env holder
matthargett Jun 5, 2026
7b20e53
docs(roadmap): record node-api-cts FetchContent evaluation (task 6) —…
matthargett Jun 5, 2026
46c7664
Android: dlopen conformance addons as dynamic .node backed by a share…
matthargett Jun 5, 2026
ed90fd5
docs(roadmap): Android uses dynamic .node + shared libnapi.so (aligns…
matthargett Jun 5, 2026
4fc648a
Android tests: use AndroidExtensions StdoutLogger for stdout->logcat
matthargett Jun 5, 2026
56f26af
docs(roadmap): stdout->logcat is via AndroidExtensions StdoutLogger, …
matthargett Jun 5, 2026
6f2737b
Sync napi shared-lib change with PR #183 (gate behind JSR_NAPI_SHARED…
matthargett Jun 5, 2026
94d7818
Tests: enable the v5-clean reference double-free conformance test
matthargett Jun 5, 2026
c3d885f
docs(roadmap): reference-test staging + GC-safety review (re hermes-w…
matthargett Jun 5, 2026
2867083
Node-API: address #116 review (JSC call dispatch, status type, Window…
matthargett Jun 5, 2026
63e1604
Keep JSI's napi.h self-contained (restores upstream's inlined type copy)
matthargett Sep 1, 2026
a197dc2
Tests/NodeApi: add missing <cstdint> in child_process.h (fixes Linux …
matthargett Jun 5, 2026
ddd6dc6
Tests/node_lite: select the napi engine by compile-define, not platform
matthargett Jun 5, 2026
4137a99
Tests/NodeApi: link napi into addons on Windows/UWP so napi_* resolve…
matthargett Jun 5, 2026
908851b
Tests: don't build the C-API conformance harness for the JSI backend
matthargett Sep 1, 2026
256daf9
Don't hard-fail the Linux JSC library probe (breaks the Hermes host b…
matthargett Sep 1, 2026
b059acf
Tests/Android: build the on-device Node-API harness only for validate…
matthargett Sep 1, 2026
ad6bfbc
Don't default napi to a shared library for the Hermes engine on Android
matthargett Sep 1, 2026
4e78dd1
Node-API: raise default NAPI_VERSION 5 -> 7
matthargett Jun 5, 2026
81bc20d
JSC: implement napi_set/get_instance_data (N-API v6)
matthargett Jun 5, 2026
22a6b6a
JSC: implement N-API v6 BigInt + v7 ArrayBuffer detach
matthargett Jun 5, 2026
eb9415c
tests: note detach/property_names conformance tests are v9-gated (nod…
matthargett Jun 5, 2026
c7c18e2
JSC: detect BigInt in napi_typeof on jsc-android (cached typeof predi…
matthargett Jun 5, 2026
d31f0c0
Chakra/JSI: N-API v6/v7 surface for the cross-engine version bump
matthargett Jun 5, 2026
b5b3a51
Tests/Android: run test_bigint on V8 only (jsc-android ~2020 BigInt s…
matthargett Jun 5, 2026
47c1919
JSC: feature-detect BigInt; throw ENOTSUP where the engine lacks it (…
matthargett Jun 5, 2026
dd66a3f
Tests/NodeApi: add literal-free test_bigint_unsupported fallback; gat…
matthargett Jun 5, 2026
ba7054f
Tests/Android: surface in-process node_lite failure detail to logcat
matthargett Jun 5, 2026
affe586
Tests/NodeApi: gate the v6/v7 conformance addons by an engine allow-list
matthargett Sep 1, 2026
3a8ea84
Tests/Android: apply the same v6/v7 engine allow-list to the on-devic…
matthargett Sep 1, 2026
434fd79
Tests/Android: keep the v6/v7 test list inside the harness guard
matthargett Sep 1, 2026
61ce046
Tests/Android: fix the dead guard that silently disabled the on-devic…
matthargett Sep 1, 2026
19fc223
Core/Node-API: pin jsc-android to r294992, the newest build, which ha…
matthargett Sep 1, 2026
b8a002c
JSC: resolve BigInt and ArrayBuffer detach through intrinsics capture…
matthargett Sep 1, 2026
ae0f6d3
Core/Node-API: bump the node-addon-api header to NAPI_VERSION 7
matthargett Sep 1, 2026
e2098c3
JSC: keep the napi_typeof BigInt fallback off the object path
matthargett Sep 1, 2026
15442b5
Tests: cover napi_detach_arraybuffer, the API that defines N-API v7
matthargett Sep 1, 2026
9adc234
V8: napi_is_detached_arraybuffer reported every ArrayBuffer as detached
matthargett Sep 1, 2026
5cb1587
Chakra: implement the N-API v7 ArrayBuffer detach pair
matthargett Sep 1, 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