Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
201 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
b5d69a8
Add optional WHATWG Streams polyfill
matthargett Jul 22, 2026
9dc464a
Keep Streams implementations internally consistent
matthargett Jul 22, 2026
54c2d6e
Preserve Streams bundle bytes across source splits
matthargett Jul 26, 2026
c53aca4
Add optional WHATWG Streams polyfill
matthargett Jul 22, 2026
bfcf0fb
Add browser-compatible compression streams
matthargett Jul 22, 2026
9fe3bb4
Keep Streams implementations internally consistent
matthargett Jul 22, 2026
b6089cd
Preserve Streams bundle bytes across source splits
matthargett Jul 26, 2026
5cc0117
Add optional WHATWG Streams polyfill
matthargett Jul 22, 2026
226ed90
Complete Blob streaming and slicing support
matthargett Jul 22, 2026
aeffa22
Keep Streams implementations internally consistent
matthargett Jul 22, 2026
1da1abb
Write Blob BYOB reads into caller buffers
matthargett Jul 22, 2026
34a7ead
Preserve Streams bundle bytes across source splits
matthargett Jul 26, 2026
c3d924e
Avoid per-stream Blob callback wrappers
matthargett Jul 26, 2026
f23e672
Use JSC-compatible Blob stream state key
matthargett Jul 26, 2026
760ae3c
Share Blob stream source callbacks
matthargett Jul 26, 2026
2fa29ef
Add optional WHATWG Streams polyfill
matthargett Jul 22, 2026
6f460de
Complete Blob streaming and slicing support
matthargett Jul 22, 2026
c3c0a76
Add browser-compatible Headers and Response
matthargett Jul 22, 2026
8cd66a6
Handle empty Response byte bodies
matthargett Jul 22, 2026
ac02941
Keep Streams implementations internally consistent
matthargett Jul 22, 2026
35d5a9f
Track Response body disturbance through stream operations
matthargett Jul 22, 2026
c838733
Write Blob BYOB reads into caller buffers
matthargett Jul 22, 2026
8ecb9df
Preserve Streams bundle bytes across source splits
matthargett Jul 26, 2026
29e0357
Add optional WHATWG Streams polyfill
matthargett Jul 22, 2026
8e8ff8f
Complete Blob streaming and slicing support
matthargett Jul 22, 2026
f16e5a7
Add browser-compatible Headers and Response
matthargett Jul 22, 2026
4b2961d
Handle empty Response byte bodies
matthargett Jul 22, 2026
476cc2b
Support data URLs in the fetch polyfill
matthargett Jul 22, 2026
e04a95f
Avoid intermediate data URL decode buffers
matthargett Jul 22, 2026
a823b2f
Keep Streams implementations internally consistent
matthargett Jul 22, 2026
e29e752
Write Blob BYOB reads into caller buffers
matthargett Jul 22, 2026
006a18d
Track Response body disturbance through stream operations
matthargett Jul 22, 2026
517b298
Preserve Streams bundle bytes across source splits
matthargett Jul 26, 2026
9b273a4
Streams: emit the embedded bundle as <=16 KB literals; link Streams i…
matthargett Sep 13, 2026
1e7557b
Streams: emit the embedded bundle as <=16 KB literals; link Streams i…
matthargett Sep 13, 2026
d3d7592
Streams: emit the embedded bundle as <=16 KB literals; link Streams i…
matthargett Sep 13, 2026
4709fac
Streams: emit the embedded bundle as <=16 KB literals; link Streams i…
matthargett Sep 13, 2026
242dccf
Streams: emit the embedded bundle as <=16 KB literals; link Streams i…
matthargett Sep 13, 2026
1d88a3e
Tests/Android: link Compression into the test host for the Shared.cpp…
matthargett Sep 13, 2026
5d4affa
Blob: cast the BYOB respond() length to double (MSVC C4244 as error)
matthargett Sep 13, 2026
df025b3
Blob: cast the BYOB respond() length to double (MSVC C4244 as error)
matthargett Sep 13, 2026
317dbda
Fetch: emit the embedded polyfill as adjacent <=16 KB literals (MSVC …
matthargett Sep 13, 2026
160557a
Fetch: emit the embedded polyfill as adjacent <=16 KB literals (MSVC …
matthargett Sep 13, 2026
b8c4ab3
Blob: cast the BYOB respond() length to double (MSVC C4244 as error)
matthargett Sep 13, 2026
b397862
Compression: throw through the Napi wrapper and link zlib by name
matthargett Sep 13, 2026
c52e3ee
Blob: read DataView parts through JS properties (JSI's Napi::DataView…
matthargett Sep 13, 2026
ec9bacb
Blob: read DataView parts through JS properties (JSI's Napi::DataView…
matthargett Sep 13, 2026
c4a7a63
CI: suppress WebKitGTK-internal LeakSanitizer reports on the Linux sa…
matthargett Sep 13, 2026
f898cec
Blob: read DataView parts through JS properties (JSI's Napi::DataView…
matthargett Sep 13, 2026
8163ab8
CI: suppress WebKitGTK-internal LeakSanitizer reports on the Linux sa…
matthargett Sep 13, 2026
d667e48
Tests: give the heavy Blob/Response stream tests a 60 s budget (timed…
matthargett Sep 13, 2026
bcf20e6
Tests: give the heavy Blob/Response stream tests a 60 s budget (timed…
matthargett Sep 13, 2026
1099450
Tests: give the heavy Blob/Response stream tests a 60 s budget (timed…
matthargett Sep 13, 2026
bb817d0
QuickJS: bound the JS stack to the thread's real stack; Chakra: defin…
matthargett Sep 13, 2026
30eebd2
QuickJS: bound the JS stack to the thread's real stack; Chakra: defin…
matthargett Sep 13, 2026
810a092
QuickJS: bound the JS stack to the thread's real stack; Chakra: defin…
matthargett Sep 13, 2026
fef8cb9
Address review: define _GNU_SOURCE before system headers, bound the f…
matthargett Sep 13, 2026
5cd790d
QuickJS: keep <windows.h> from defining min/max (Win32 QuickJS build)
matthargett Sep 13, 2026
d1ca32e
QuickJS: measure the JS stack budget from the current stack pointer t…
matthargett Sep 13, 2026
f30a41c
Resolve data: URLs for every UrlLib consumer via a registered scheme …
matthargett Sep 13, 2026
707479d
Address review: define _GNU_SOURCE before system headers, bound the f…
matthargett Sep 13, 2026
f6e087b
QuickJS: keep <windows.h> from defining min/max (Win32 QuickJS build)
matthargett Sep 13, 2026
dbbb166
QuickJS: measure the JS stack budget from the current stack pointer t…
matthargett Sep 13, 2026
e5a9663
Address review: define _GNU_SOURCE before system headers, bound the f…
matthargett Sep 13, 2026
f5b43de
QuickJS: keep <windows.h> from defining min/max (Win32 QuickJS build)
matthargett Sep 13, 2026
6e662d7
QuickJS: measure the JS stack budget from the current stack pointer t…
matthargett Sep 13, 2026
6e094fa
QuickJS: run the Android environment on a nested 8 MiB-stack thread i…
matthargett Sep 13, 2026
c502dcb
QuickJS: raise the nested-thread JS stack limit to 6 MiB for deep rec…
matthargett Sep 13, 2026
fe7691b
QuickJS: nested 8 MiB Android thread + 6 MiB JS stack limit (supersed…
matthargett Sep 13, 2026
5d7db94
QuickJS: nested 8 MiB Android thread + 6 MiB JS stack limit (supersed…
matthargett Sep 13, 2026
f8c8335
Node-API (JavaScriptCore): only erase the environment registry entry …
matthargett Sep 14, 2026
cefd456
JSC Node-API: reference primitives and coerce property receivers
matthargett Sep 14, 2026
1ff5263
Performance: keep the time origin per environment
matthargett Sep 14, 2026
2a07b6a
Tests: cover primitive exceptions, receivers and references in Node-API
matthargett Sep 14, 2026
c3893a4
Compression: stage output bytes, not Uint8Arrays, until the enqueue c…
matthargett Sep 14, 2026
f8e8152
JSC Node-API: address review — dead weak targets, primitive receivers…
matthargett Sep 14, 2026
6f4e7e2
Node-API (JavaScriptCore): guard the environment registry with a mutex
matthargett Sep 14, 2026
5c6cd00
Tests: cover the per-environment performance.now() origin
matthargett Sep 14, 2026
cf3904a
Blob/URL: identify Blobs by our own constructor; make URL's static me…
matthargett Sep 17, 2026
b374eb4
Tests: reach the global object without globalThis (ChakraCore lacks it)
matthargett Sep 17, 2026
e27baca
Node-API-JSI: honour napi_writable for method properties; assert the …
matthargett Sep 17, 2026
a5c341d
Blob: pin the constructor under a hidden global instead of the JsRunt…
matthargett Sep 17, 2026
78a9a7f
Blob: hoist the constructor-key constants to file scope (fix the build)
matthargett Sep 17, 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
fc09b19
Merge remote-tracking branch 'origin/web-streams-polyfill' into ww-re…
matthargett Sep 26, 2026
c6bdf4a
Merge remote-tracking branch 'origin/compression-stream-polyfill' int…
matthargett Sep 26, 2026
5dd50a1
Merge remote-tracking branch 'origin/blob-stream-polyfill' into ww-re…
matthargett Sep 26, 2026
9a33540
Merge remote-tracking branch 'origin/fetch-response-headers-polyfill'…
matthargett Sep 26, 2026
92b9631
Merge remote-tracking branch 'origin/fetch-data-url-polyfill' into ww…
matthargett Sep 26, 2026
891e1e0
Merge remote-tracking branch 'origin/engine-fixes' into ww-restack
matthargett Sep 26, 2026
4bf2e8c
Merge remote-tracking branch 'origin/jsc-env-registry-guard' into ww-…
matthargett Sep 26, 2026
85bf2a4
Merge remote-tracking branch 'origin/jsc-napi-primitive-values' into …
matthargett Sep 26, 2026
65f9b7c
Merge remote-tracking branch 'origin/performance-per-env-time-origin'…
matthargett Sep 26, 2026
685147e
Merge remote-tracking branch 'origin/blob-identity-url-writable-stati…
matthargett Sep 26, 2026
688f044
Restack Worker after CTS staging fix
matthargett Jul 26, 2026
213957b
Support visualization worker startup
matthargett Jul 26, 2026
9336a57
Trace Worker WPT lifecycle progress
matthargett Jul 26, 2026
9fdfbd2
Fix JSC global event handler accessors
matthargett Jul 26, 2026
91500e8
Improve Worker bootstrap diagnostics
matthargett Jul 26, 2026
29ada33
Support visualization Worker cache hydration
matthargett Jul 26, 2026
a57d536
Mirror visualization cache batch hydration
matthargett Jul 26, 2026
06c9cbd
Preserve primitive abort reasons safely
matthargett Jul 26, 2026
92dda88
Honor Node-API function name lengths
matthargett Jul 26, 2026
d5b6285
Add an in-memory IndexedDB polyfill
matthargett Jul 26, 2026
21ec8c8
Compose Worker with standalone browser polyfills
matthargett Jul 26, 2026
865b368
Fix IndexedDB bundle portability
matthargett Jul 26, 2026
20694e0
Support IndexedDB on older ChakraCore
matthargett Jul 26, 2026
5a9a469
Replace vendored WPT with focused Worker regressions
matthargett Jul 27, 2026
baf22eb
Pump the Apple run loop while draining JavaScriptCore tasks
matthargett Jul 22, 2026
1a2e696
Integration: drop duplicates reintroduced by the Worker cherry-picks …
matthargett Sep 13, 2026
c91c6f6
Integration: restore Polyfills/CMakeLists.txt (the sibling-branch mer…
matthargett Sep 13, 2026
da129ea
Worker: split the embedded script bundles into <=15 KB literals (MSVC…
matthargett Sep 13, 2026
32b0d88
Build: raise the Apple deployment floor to iOS 13 / macOS 10.15 for s…
matthargett Sep 13, 2026
7845f16
JSC: re-arm the execution-time-limit watchdog after every declined poll
matthargett Sep 14, 2026
d95c0d2
Tests: resolve the Worker ScriptRoot from the executable, not the wor…
matthargett Sep 14, 2026
5e5edfd
Tests: locate the executable with GetModuleFileNameW on Windows; prin…
matthargett Sep 14, 2026
9cf101c
CI (iOS): take the exit code from the last stderr line
matthargett Sep 14, 2026
adcfa6c
CI (Linux): use polling VM traps under ThreadSanitizer
matthargett Sep 14, 2026
f19c9a4
Worker: treat { type: undefined } as the classic default
matthargett Sep 16, 2026
dd4e8d7
Polyfills/CMakeLists.txt: take the integration branch's copy (the uni…
matthargett Sep 26, 2026
f47cce1
Merge napi-v7 (5cd8bfa: upstream main 62818ee) into the Worker stack;…
matthargett Sep 26, 2026
28f9deb
Merge upstream main (62818ee) and adopt the per-feature test layout (…
matthargett Sep 26, 2026
f6d6690
Merge upstream main (62818ee) and adopt the per-feature test layout (…
matthargett Sep 26, 2026
28f5438
Merge upstream main (62818ee) and adopt the per-feature test layout (…
matthargett Sep 26, 2026
e1531e1
Merge upstream main (62818ee) and adopt the per-feature test layout (…
matthargett Sep 26, 2026
f917f4c
Merge upstream main (62818ee) and adopt the per-feature test layout (…
matthargett Sep 26, 2026
1993ce4
Merge upstream main (62818ee) and adopt the per-feature test layout (…
matthargett Sep 26, 2026
7de804c
Merge upstream main (62818ee) and adopt the per-feature test layout (…
matthargett Sep 26, 2026
5dde0df
Merge upstream main (62818ee) and adopt the per-feature test layout (…
matthargett Sep 26, 2026
f318237
Merge the re-homed web-streams-polyfill head (upstream main 62818ee) …
matthargett Sep 26, 2026
42fc1b4
Merge the re-homed compression-stream-polyfill head (upstream main 62…
matthargett Sep 26, 2026
3073404
Merge the re-homed blob-stream-polyfill head (upstream main 62818ee) …
matthargett Sep 26, 2026
f2704de
Merge the re-homed fetch-response-headers-polyfill head (upstream mai…
matthargett Sep 26, 2026
7bd796a
Merge the re-homed fetch-data-url-polyfill head (upstream main 62818e…
matthargett Sep 26, 2026
353b439
Merge the re-homed jsc-napi-primitive-values head (upstream main 6281…
matthargett Sep 26, 2026
095c35e
Merge the re-homed performance-per-env-time-origin head (upstream mai…
matthargett Sep 26, 2026
54845b2
Merge the re-homed blob-identity-url-writable-statics head (upstream …
matthargett Sep 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions .github/lsan_suppressions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# LeakSanitizer suppressions for the Linux sanitizer job.
#
# WebKitGTK's JavaScriptCore grows global bookkeeping (e.g. WTF::BitVector under
# JSObjectMake -> JSObject::setPrototypeDirect on a Structure transition) that it never
# frees at exit, and LSan reports it as a direct leak attributed to whichever caller
# happened to trigger the growth -- here napi reference creation. Not our allocation.
leak:libjavascriptcoregtk
35 changes: 33 additions & 2 deletions .github/workflows/build-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,36 @@ jobs:
- name: Run Tests
run: |
xcrun simctl install booted "Build/iOS/Tests/UnitTests/RelWithDebInfo-iphonesimulator/UnitTests.app"
xcrun simctl launch --console booted "com.jsruntimehost.unittests" 2> /tmp/exitCode
(exit $(cat /tmp/exitCode))
# The app reports its exit code as the last line of stderr (see iOS/App.mm). Tests also
# write progress there (the Worker WPT runner does), so only the last line is the code:
# handing the whole capture to `exit` made every run with such output fail with 255.
xcrun simctl launch --console booted "com.jsruntimehost.unittests" 2> /tmp/stderr
cat /tmp/stderr
(exit "$(tail -n 1 /tmp/stderr)")

# A crashed app writes nothing to the exit-code pipe (the step then fails with 255) and the
# console shows only what was flushed before the crash; the simulator's crash report is the
# only record of where it died.
- name: Collect crash reports
if: failure()
run: |
for report in $(ls -t ~/Library/Logs/DiagnosticReports/UnitTests-*.ips 2>/dev/null | head -n 3); do
echo "::group::$report"
python3 - "$report" <<'EOF'
import json, sys
raw = open(sys.argv[1]).read()
header, body = raw.split("\n", 1)
crash = json.loads(body)
images = crash.get("usedImages", [])
print("exception:", json.dumps(crash.get("exception")), "termination:", json.dumps(crash.get("termination")))
faulting = crash.get("faultingThread", 0)
for index, thread in enumerate(crash.get("threads", [])):
if index != faulting and not thread.get("triggered"):
continue
print(f"--- thread {index} {thread.get('name') or ''} {thread.get('queue') or ''}")
for frame in thread.get("frames", [])[:40]:
image = images[frame.get("imageIndex", 0)] if frame.get("imageIndex", 0) < len(images) else {}
print(f" {image.get('name', '?'):28s} {frame.get('symbol', '?')}+{frame.get('symbolLocation', 0)}")
EOF
echo "::endgroup::"
done
6 changes: 6 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ jobs:
run: ./UnitTests
env:
TSAN_OPTIONS: ${{ inputs.enable-thread-sanitizer && format('suppressions={0}/.github/tsan_suppressions.txt', github.workspace) || '' }}
LSAN_OPTIONS: ${{ inputs.enable-sanitizers && format('suppressions={0}/.github/lsan_suppressions.txt', github.workspace) || '' }}
# JSC's concurrent GC on Linux uses SIGUSR1 + sem_wait to suspend mutator
# threads at safepoints. TSan's signal interception delays SIGUSR1 delivery
# indefinitely, deadlocking the Collector Thread's sem_wait. Disabling the
Expand All @@ -62,3 +63,8 @@ jobs:
# thread_suspend() and is unaffected.
JSC_useConcurrentGC: ${{ inputs.enable-thread-sanitizer && '0' || '' }}

# VM traps (how the execution-time-limit watchdog stops a running script) are delivered
# by signal on Linux, which TSan likewise never lets through: a terminated run-forever
# worker then spins until the test's runtime teardown joins it, and the job hangs after
# the Worker WPT suite has otherwise finished. Polling traps need no signal.
JSC_usePollingTraps: ${{ inputs.enable-thread-sanitizer && '1' || '' }}
22 changes: 21 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ FetchContent_Declare(UrlLib
GIT_REPOSITORY https://github.com/BabylonJS/UrlLib.git
GIT_TAG 0c991337a1160ba7a2d062bf8e342d0a66f48dc9
EXCLUDE_FROM_ALL)
FetchContent_Declare(zlib
URL "https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz"
EXCLUDE_FROM_ALL)
FetchContent_Declare(quickjs-ng
GIT_REPOSITORY https://github.com/quickjs-ng/quickjs.git
GIT_TAG 93d3f7df465027f487ed37e175a0bc3012fee79e
Expand All @@ -64,7 +67,9 @@ if(IOS)
FetchContent_MakeAvailable_With_Message(ios-cmake)
set(CMAKE_TOOLCHAIN_FILE "${ios-cmake_SOURCE_DIR}/ios.toolchain.cmake" CACHE PATH "")
set(PLATFORM "OS64COMBINED" CACHE STRING "")
set(DEPLOYMENT_TARGET "12" CACHE STRING "")
# std::filesystem (used by the Worker polyfill) is unavailable below iOS 13 / macOS 10.15,
# and the project floor is well above iOS 12 regardless; keep 13 as the minimum.
set(DEPLOYMENT_TARGET "13" CACHE STRING "")
set(CMAKE_XCODE_GENERATE_SCHEME TRUE)
endif()

Expand Down Expand Up @@ -102,6 +107,10 @@ option(JSRUNTIMEHOST_POLYFILL_FILE "Include JsRuntimeHost Polyfill File and File
option(JSRUNTIMEHOST_POLYFILL_PERFORMANCE "Include JsRuntimeHost Polyfill Performance." ON)
option(JSRUNTIMEHOST_POLYFILL_TEXTDECODER "Include JsRuntimeHost Polyfill TextDecoder." ON)
option(JSRUNTIMEHOST_POLYFILL_TEXTENCODER "Include JsRuntimeHost Polyfill TextEncoder." ON)
option(JSRUNTIMEHOST_POLYFILL_STREAMS "Include JsRuntimeHost Polyfill Web Streams." ON)
option(JSRUNTIMEHOST_POLYFILL_COMPRESSION "Include JsRuntimeHost Polyfills CompressionStream and DecompressionStream." ON)
option(JSRUNTIMEHOST_POLYFILL_WORKER "Include JsRuntimeHost Polyfill Worker." ON)
option(JSRUNTIMEHOST_POLYFILL_INDEXEDDB "Include JsRuntimeHost Polyfill IndexedDB." ON)

# Sanitizers
option(ENABLE_SANITIZERS "Enable AddressSanitizer and UBSan" OFF)
Expand Down Expand Up @@ -154,6 +163,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
2 changes: 2 additions & 0 deletions Core/AppRuntime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ if(NAPI_JAVASCRIPT_ENGINE STREQUAL "V8" AND JSRUNTIMEHOST_CORE_APPRUNTIME_V8_INS
set_property(TARGET v8inspector PROPERTY FOLDER Dependencies)
elseif(NAPI_JAVASCRIPT_ENGINE STREQUAL "QuickJS")
target_link_libraries(AppRuntime PRIVATE qjs)
elseif(NAPI_JAVASCRIPT_ENGINE STREQUAL "JavaScriptCore" AND UNIX AND NOT APPLE)
target_link_libraries(AppRuntime PRIVATE ${CMAKE_DL_LIBS})
endif()

set_property(TARGET AppRuntime PROPERTY FOLDER Core)
Expand Down
24 changes: 24 additions & 0 deletions Core/AppRuntime/Include/Babylon/AppRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ namespace Babylon
// Optional handler for unhandled exceptions.
std::function<void(const Napi::Error&)> UnhandledExceptionHandler{DefaultUnhandledExceptionHandler};

// Optional final runtime-thread notification after engine and
// environment teardown. The callback is the thread's last action
// and must not access that runtime's Napi objects.
std::function<void()> ThreadExitHandler{};

// Defines whether to enable the debugger. Only implemented for V8 and Chakra.
bool EnableDebugger{false};

Expand All @@ -48,6 +53,17 @@ namespace Babylon
void Suspend();
void Resume();

// Permanently stop accepting work and exit after the currently
// executing dispatch returns. Unlike Terminate(), this does not
// interrupt JavaScript in the middle of its current task.
void Close();

// Permanently stop accepting work and request interruption of any
// JavaScript currently executing. The interruption is immediate on
// engines with an interrupt hook (including system JavaScriptCore) and
// cooperative between dispatches on the remaining engines.
void Terminate();

void Dispatch(Dispatchable<void(Napi::Env)> callback);

// Default unhandled exception handler that outputs the error message to the program output.
Expand Down Expand Up @@ -87,6 +103,14 @@ namespace Babylon

Internal::DelayedTaskScheduler& GetDelayedTaskScheduler();

// Engine tiers may query the shared termination flag without exposing
// engine types in the public API.
bool IsTerminationRequested() const noexcept;

// Execution watchdogs use a separate flag so Close() can finish the
// current task while Terminate() can still interrupt a tight loop.
bool IsExecutionTerminationRequested() const noexcept;

Options m_options;

class Impl;
Expand Down
59 changes: 47 additions & 12 deletions Core/AppRuntime/Source/AppRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <arcana/threading/dispatcher.h>

#include <cassert>
#include <atomic>
#include <optional>
#include <mutex>
#include <thread>
Expand Down Expand Up @@ -40,6 +41,8 @@ namespace Babylon
std::unique_ptr<Internal::DelayedTaskScheduler> m_delayedTaskScheduler{std::make_unique<Internal::DelayedTaskScheduler>()};
bool m_delayedTaskSchedulerRegistered{};
std::thread m_thread;
std::atomic_bool m_terminationRequested{false};
std::atomic_bool m_executionTerminationRequested{false};
};

AppRuntime::AppRuntime() :
Expand All @@ -51,7 +54,13 @@ namespace Babylon
: m_options{std::move(options)}
, m_impl{std::make_unique<Impl>()}
{
m_impl->m_thread = std::thread{[this] { RunPlatformTier(); }};
m_impl->m_thread = std::thread{[this] {
RunPlatformTier();
if (m_options.ThreadExitHandler)
{
m_options.ThreadExitHandler();
}
}};

Dispatch([this](Napi::Env env) {
JsRuntime::CreateForJavaScript(env, [this](auto func) { Dispatch(std::move(func)); });
Expand All @@ -67,17 +76,7 @@ namespace Babylon
m_impl->m_suspensionLock.reset();
}

// Cancel immediately so pending work is dropped promptly, then append
// a no-op work item to wake the worker thread from blocking_tick. The
// no-op goes through push() which acquires the queue mutex, avoiding
// the race where a bare notify_all() can be missed by wait().
//
// NOTE: This preserves the existing shutdown behavior where pending
// callbacks are dropped on cancellation. A more complete solution
// would add cooperative shutdown (e.g. NotifyDisposing/Rundown) so
// consumers can finish cleanup work before the runtime is destroyed.
m_impl->m_cancelSource.cancel();
m_impl->Append([](Napi::Env) {});
Terminate();

m_impl->m_thread.join();
}
Expand Down Expand Up @@ -126,8 +125,44 @@ namespace Babylon
m_impl->m_suspensionLock.reset();
}

void AppRuntime::Terminate()
{
m_impl->m_executionTerminationRequested.store(true);
Close();
}

void AppRuntime::Close()
{
if (m_impl->m_terminationRequested.exchange(true))
{
return;
}

m_impl->m_cancelSource.cancel();

// Queueing under the dispatcher's mutex makes the wake-up immune to
// the missed-notification race covered by DestroyDoesNotDeadlock.
// The cancelled run loop drops this no-op rather than executing it.
m_impl->m_dispatcher.queue([]() {});
}

bool AppRuntime::IsTerminationRequested() const noexcept
{
return m_impl->m_terminationRequested.load();
}

bool AppRuntime::IsExecutionTerminationRequested() const noexcept
{
return m_impl->m_executionTerminationRequested.load();
}

void AppRuntime::Dispatch(Dispatchable<void(Napi::Env)> func)
{
if (IsTerminationRequested())
{
return;
}

m_impl->Append([this, func{std::move(func)}](Napi::Env env) mutable {
Execute([this, env, func{std::move(func)}]() mutable {
// Some engines (notably Hermes) require an open NAPI handle
Expand Down
104 changes: 103 additions & 1 deletion Core/AppRuntime/Source/AppRuntime_JavaScriptCore.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,101 @@
#include "AppRuntime.h"
#include <napi/env.h>

#if __has_include(<JavaScriptCore/JSContextRefPrivate.h>)
#include <JavaScriptCore/JSContextRefPrivate.h>
#define JSRUNTIMEHOST_HAS_JSC_EXECUTION_TIME_LIMIT 1
#elif defined(__unix__) || defined(__APPLE__)
// Apple's SDK ships no JSContextRefPrivate.h either, but the system JavaScriptCore framework
// exports the same C ABI (it is SPI there); resolving it dynamically keeps Worker::terminate()
// able to stop a tight loop, exactly as on WebKitGTK. Without it a terminated worker that never
// yields keeps its thread forever and the finalizer's join deadlocks at teardown.
#include <dlfcn.h>
#define JSRUNTIMEHOST_LOOKUP_JSC_EXECUTION_TIME_LIMIT 1
#endif

namespace
{
#if defined(JSRUNTIMEHOST_HAS_JSC_EXECUTION_TIME_LIMIT) || \
defined(JSRUNTIMEHOST_LOOKUP_JSC_EXECUTION_TIME_LIMIT)
using ShouldTerminateCallback = bool (*)(JSContextRef, void*);
using SetExecutionTimeLimit = void (*)(JSContextGroupRef, double, ShouldTerminateCallback, void*);
using ClearExecutionTimeLimit = void (*)(JSContextGroupRef);

// How often running script is polled for Terminate().
constexpr double ExecutionTimeLimitSeconds{0.05};

struct ExecutionWatchdog
{
Babylon::AppRuntime* Runtime{};
SetExecutionTimeLimit Set{};
ClearExecutionTimeLimit Clear{};
ShouldTerminateCallback Callback{};

bool Available() const
{
return Set != nullptr && Clear != nullptr;
}

// JavaScriptCore restarts the watchdog timer only when the callback sets a new limit
// itself: its "callback did nothing" branch takes the CPU deadline that has just expired
// for a timer the callback started and schedules nothing, so a single declined poll would
// otherwise silence the watchdog until the running script returns on its own.
void Rearm(JSContextRef context)
{
Set(JSContextGetGroup(context), ExecutionTimeLimitSeconds, Callback, this);
}
};
#endif
}

#if __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#endif

namespace Babylon
{
void AppRuntime::RunEnvironmentTier(const char*)
{
auto globalContext = JSGlobalContextCreateInGroup(nullptr, nullptr);

#if defined(JSRUNTIMEHOST_HAS_JSC_EXECUTION_TIME_LIMIT) || \
defined(JSRUNTIMEHOST_LOOKUP_JSC_EXECUTION_TIME_LIMIT)
auto contextGroup = JSContextGetGroup(globalContext);
ExecutionWatchdog watchdog{this};
#if defined(JSRUNTIMEHOST_HAS_JSC_EXECUTION_TIME_LIMIT)
watchdog.Set = &JSContextGroupSetExecutionTimeLimit;
watchdog.Clear = &JSContextGroupClearExecutionTimeLimit;
#else
// WebKitGTK deliberately omits JSContextRefPrivate.h from its dev
// package, but current system builds export the same C ABI. Resolve it
// dynamically so JsRuntimeHost stays buildable against the public
// package and gracefully falls back to between-dispatch termination on
// older builds that do not export the watchdog.
watchdog.Set = reinterpret_cast<SetExecutionTimeLimit>(
dlsym(RTLD_DEFAULT, "JSContextGroupSetExecutionTimeLimit"));
watchdog.Clear = reinterpret_cast<ClearExecutionTimeLimit>(
dlsym(RTLD_DEFAULT, "JSContextGroupClearExecutionTimeLimit"));
#endif
// Poll at a modest interval while JS is running. Returning true from
// this callback raises a catchable termination exception (a bare
// string, "JavaScript execution terminated.") and lets the AppRuntime
// thread unwind, so Worker::terminate() also stops a tight loop that
// never reaches the dispatch queue.
watchdog.Callback = [](JSContextRef context, void* data) {
auto* watchdog = static_cast<ExecutionWatchdog*>(data);
if (watchdog->Runtime->IsExecutionTerminationRequested())
{
return true;
}
watchdog->Rearm(context);
return false;
};
if (watchdog.Available())
{
watchdog.Set(contextGroup, ExecutionTimeLimitSeconds, watchdog.Callback, &watchdog);
}
#endif

#if __APPLE__
if (__builtin_available(iOS 16.4, macOS 13.3, *))
{
Expand All @@ -18,6 +107,14 @@ namespace Babylon

Run(env);

#if defined(JSRUNTIMEHOST_HAS_JSC_EXECUTION_TIME_LIMIT) || \
defined(JSRUNTIMEHOST_LOOKUP_JSC_EXECUTION_TIME_LIMIT)
if (watchdog.Available())
{
watchdog.Clear(contextGroup);
}
#endif

JSGlobalContextRelease(globalContext);

// Detach must come after JSGlobalContextRelease since it triggers finalizers which require env.
Expand All @@ -30,6 +127,11 @@ namespace Babylon

void AppRuntime::DrainMicrotasks(Napi::Env)
{
// JavaScriptCore drains microtasks automatically at script boundaries.
// JavaScriptCore drains Promise jobs at script boundaries, but Apple
// platform work such as asynchronous WebAssembly compilation completes
// through the current thread's run loop.
#if __APPLE__
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
#endif
}
}
Loading