@@ -128,6 +128,12 @@ typedef struct {
128128 JSValue func ;
129129} JSOSTimer ;
130130
131+ typedef struct {
132+ struct list_head link ;
133+ JSValue promise ;
134+ JSValue reason ;
135+ } JSRejectedPromiseEntry ;
136+
131137#ifdef USE_WORKER
132138
133139typedef struct {
@@ -168,6 +174,7 @@ typedef struct JSThreadState {
168174 struct list_head os_signal_handlers ; /* list JSOSSignalHandler.link */
169175 struct list_head os_timers ; /* list of JSOSTimer.link */
170176 struct list_head port_list ; /* list of JSWorkerMessageHandler.link */
177+ struct list_head rejected_promise_list ; /* list of JSRejectedPromiseEntry.link */
171178 int eval_script_recurse ; /* only used in the main thread */
172179 int64_t next_timer_id ; /* for setTimeout / setInterval */
173180 bool can_js_os_poll ;
@@ -4280,6 +4287,7 @@ void js_std_init_handlers(JSRuntime *rt)
42804287 init_list_head (& ts -> os_signal_handlers );
42814288 init_list_head (& ts -> os_timers );
42824289 init_list_head (& ts -> port_list );
4290+ init_list_head (& ts -> rejected_promise_list );
42834291
42844292 ts -> next_timer_id = 1 ;
42854293
@@ -4299,6 +4307,14 @@ void js_std_init_handlers(JSRuntime *rt)
42994307#endif
43004308}
43014309
4310+ static void free_rp (JSRuntime * rt , JSRejectedPromiseEntry * rp )
4311+ {
4312+ list_del (& rp -> link );
4313+ JS_FreeValueRT (rt , rp -> promise );
4314+ JS_FreeValueRT (rt , rp -> reason );
4315+ js_free_rt (rt , rp );
4316+ }
4317+
43024318void js_std_free_handlers (JSRuntime * rt )
43034319{
43044320 JSThreadState * ts = js_get_thread_state (rt );
@@ -4319,6 +4335,11 @@ void js_std_free_handlers(JSRuntime *rt)
43194335 free_timer (rt , th );
43204336 }
43214337
4338+ list_for_each_safe (el , el1 , & ts -> rejected_promise_list ) {
4339+ JSRejectedPromiseEntry * rp = list_entry (el , JSRejectedPromiseEntry , link );
4340+ free_rp (rt , rp );
4341+ }
4342+
43224343#ifdef USE_WORKER
43234344 /* XXX: free port_list ? */
43244345 js_free_message_pipe (ts -> recv_pipe );
@@ -4366,14 +4387,62 @@ void js_std_dump_error(JSContext *ctx)
43664387 JS_FreeValue (ctx , exception_val );
43674388}
43684389
4390+ static JSRejectedPromiseEntry * find_rejected_promise (JSContext * ctx , JSThreadState * ts ,
4391+ JSValueConst promise )
4392+ {
4393+ struct list_head * el ;
4394+
4395+ list_for_each (el , & ts -> rejected_promise_list ) {
4396+ JSRejectedPromiseEntry * rp = list_entry (el , JSRejectedPromiseEntry , link );
4397+ if (JS_IsSameValue (ctx , rp -> promise , promise ))
4398+ return rp ;
4399+ }
4400+ return NULL ;
4401+ }
4402+
43694403void js_std_promise_rejection_tracker (JSContext * ctx , JSValueConst promise ,
43704404 JSValueConst reason ,
43714405 bool is_handled , void * opaque )
43724406{
4373- if (!is_handled ) {
4374- fprintf (stderr , "Possibly unhandled promise rejection: " );
4375- js_std_dump_error1 (ctx , reason );
4376- fflush (stderr );
4407+
4408+ JSRuntime * rt = JS_GetRuntime (ctx );
4409+ JSThreadState * ts = js_get_thread_state (rt );
4410+ JSRejectedPromiseEntry * rp = find_rejected_promise (ctx , ts , promise );
4411+
4412+ if (is_handled ) {
4413+ /* the rejection is handled, so the entry can be removed if present */
4414+ if (rp )
4415+ free_rp (rt , rp );
4416+ } else {
4417+ /* add a new entry if needed */
4418+ if (!rp ) {
4419+ rp = js_malloc_rt (rt , sizeof (* rp ));
4420+ if (rp ) {
4421+ rp -> promise = JS_DupValue (ctx , promise );
4422+ rp -> reason = JS_DupValue (ctx , reason );
4423+ list_add_tail (& rp -> link , & ts -> rejected_promise_list );
4424+ }
4425+ }
4426+ }
4427+ }
4428+
4429+ /* check if there are pending promise rejections. It must be done
4430+ asynchrously in case a rejected promise is handled later. Currently
4431+ we do it once the application is about to sleep. It could be done
4432+ more often if needed. */
4433+ static void js_std_promise_rejection_check (JSContext * ctx )
4434+ {
4435+ JSRuntime * rt = JS_GetRuntime (ctx );
4436+ JSThreadState * ts = js_get_thread_state (rt );
4437+ struct list_head * el ;
4438+
4439+ if (unlikely (!list_empty (& ts -> rejected_promise_list ))) {
4440+ list_for_each (el , & ts -> rejected_promise_list ) {
4441+ JSRejectedPromiseEntry * rp = list_entry (el , JSRejectedPromiseEntry , link );
4442+ fprintf (stderr , "Possibly unhandled promise rejection: " );
4443+ js_std_dump_error1 (ctx , rp -> reason );
4444+ fflush (stderr );
4445+ }
43774446 exit (1 );
43784447 }
43794448}
@@ -4397,6 +4466,8 @@ int js_std_loop(JSContext *ctx)
43974466 }
43984467 }
43994468
4469+ js_std_promise_rejection_check (ctx );
4470+
44004471 if (!ts -> can_js_os_poll || js_os_poll (ctx ))
44014472 break ;
44024473 }
@@ -4431,6 +4502,8 @@ JSValue js_std_await(JSContext *ctx, JSValue obj)
44314502 if (err < 0 ) {
44324503 js_std_dump_error (ctx1 );
44334504 }
4505+ if (err == 0 )
4506+ js_std_promise_rejection_check (ctx );
44344507 if (ts -> can_js_os_poll )
44354508 js_os_poll (ctx );
44364509 } else {
0 commit comments