11#include " jsr.h"
2+ // JSGlobalContextSetUnhandledRejectionCallback lives in this private JSC header.
3+ #include < JavaScriptCore/JSContextRefPrivate.h>
4+
5+ // Native trampoline for JSC's unhandled-promise-rejection callback. JSC invokes
6+ // it with (promise, reason); we forward to the JS-side
7+ // globalThis.onUnhandledPromiseRejectionTracker (installed by ts_helpers.js),
8+ // mirroring how the V8 path routes rejections.
9+ static napi_value JscUnhandledRejectionCallback (napi_env env, napi_callback_info info) {
10+ size_t argc = 2 ;
11+ napi_value args[2 ];
12+ napi_get_cb_info (env, info, &argc, args, nullptr , nullptr );
13+
14+ napi_value global, tracker;
15+ napi_get_global (env, &global);
16+ napi_get_named_property (env, global, " onUnhandledPromiseRejectionTracker" , &tracker);
17+
18+ napi_valuetype type;
19+ napi_typeof (env, tracker, &type);
20+ if (type == napi_function) {
21+ napi_call_function (env, global, tracker, argc, args, nullptr );
22+ }
23+ return nullptr ;
24+ }
225
326napi_status js_create_runtime (jsr_ns_runtime *runtime) {
427 if (!runtime) return napi_invalid_arg;
@@ -23,6 +46,19 @@ napi_status js_create_napi_env(napi_env* env, jsr_ns_runtime runtime) {
2346 napi_get_global (*env, &global);
2447 napi_set_named_property (*env, global, " gc" , gc);
2548
49+ // Report unhandled promise rejections. JSC keeps the callback alive (it is
50+ // stored on and marked by the global object), so no extra protection is
51+ // needed. JSC only surfaces the "unhandled" event, not a later "handled"
52+ // retraction, so the JS tracker treats every call as unhandled.
53+ napi_value rejectionCallback;
54+ napi_create_function (*env, " onUnhandledRejection" , NAPI_AUTO_LENGTH ,
55+ JscUnhandledRejectionCallback, nullptr , &rejectionCallback);
56+ JSValueRef rejectionException = nullptr ;
57+ JSGlobalContextSetUnhandledRejectionCallback (
58+ (*env)->context ,
59+ reinterpret_cast <JSObjectRef>(rejectionCallback),
60+ &rejectionException);
61+
2662
2763 return napi_ok;
2864
0 commit comments