@@ -136,11 +136,18 @@ typedef struct {
136136 JSValue on_message_func ;
137137} JSWorkerMessageHandler ;
138138
139+ typedef struct {
140+ struct list_head link ;
141+ JSValue promise ;
142+ JSValue reason ;
143+ } JSRejectedPromiseEntry ;
144+
139145typedef struct JSThreadState {
140146 struct list_head os_rw_handlers ; /* list of JSOSRWHandler.link */
141147 struct list_head os_signal_handlers ; /* list JSOSSignalHandler.link */
142148 struct list_head os_timers ; /* list of JSOSTimer.link */
143149 struct list_head port_list ; /* list of JSWorkerMessageHandler.link */
150+ struct list_head rejected_promise_list ; /* list of JSRejectedPromiseEntry.link */
144151 int eval_script_recurse ; /* only used in the main thread */
145152 int next_timer_id ; /* for setTimeout() */
146153 /* not used in the main thread */
@@ -3986,6 +3993,7 @@ void js_std_init_handlers(JSRuntime *rt)
39863993 init_list_head (& ts -> os_signal_handlers );
39873994 init_list_head (& ts -> os_timers );
39883995 init_list_head (& ts -> port_list );
3996+ init_list_head (& ts -> rejected_promise_list );
39893997 ts -> next_timer_id = 1 ;
39903998
39913999 JS_SetRuntimeOpaque (rt , ts );
@@ -4023,6 +4031,13 @@ void js_std_free_handlers(JSRuntime *rt)
40234031 free_timer (rt , th );
40244032 }
40254033
4034+ list_for_each_safe (el , el1 , & ts -> rejected_promise_list ) {
4035+ JSRejectedPromiseEntry * rp = list_entry (el , JSRejectedPromiseEntry , link );
4036+ JS_FreeValueRT (rt , rp -> promise );
4037+ JS_FreeValueRT (rt , rp -> reason );
4038+ free (rp );
4039+ }
4040+
40264041#ifdef USE_WORKER
40274042 /* XXX: free port_list ? */
40284043 js_free_message_pipe (ts -> recv_pipe );
@@ -4048,13 +4063,66 @@ void js_std_dump_error(JSContext *ctx)
40484063 JS_FreeValue (ctx , exception_val );
40494064}
40504065
4066+ static JSRejectedPromiseEntry * find_rejected_promise (JSContext * ctx , JSThreadState * ts ,
4067+ JSValueConst promise )
4068+ {
4069+ struct list_head * el ;
4070+
4071+ list_for_each (el , & ts -> rejected_promise_list ) {
4072+ JSRejectedPromiseEntry * rp = list_entry (el , JSRejectedPromiseEntry , link );
4073+ if (JS_SameValue (ctx , rp -> promise , promise ))
4074+ return rp ;
4075+ }
4076+ return NULL ;
4077+ }
4078+
40514079void js_std_promise_rejection_tracker (JSContext * ctx , JSValueConst promise ,
40524080 JSValueConst reason ,
40534081 BOOL is_handled , void * opaque )
40544082{
4083+ JSRuntime * rt = JS_GetRuntime (ctx );
4084+ JSThreadState * ts = JS_GetRuntimeOpaque (rt );
4085+ JSRejectedPromiseEntry * rp ;
4086+
40554087 if (!is_handled ) {
4056- fprintf (stderr , "Possibly unhandled promise rejection: " );
4057- js_std_dump_error1 (ctx , reason );
4088+ /* add a new entry if needed */
4089+ rp = find_rejected_promise (ctx , ts , promise );
4090+ if (!rp ) {
4091+ rp = malloc (sizeof (* rp ));
4092+ if (rp ) {
4093+ rp -> promise = JS_DupValue (ctx , promise );
4094+ rp -> reason = JS_DupValue (ctx , reason );
4095+ list_add_tail (& rp -> link , & ts -> rejected_promise_list );
4096+ }
4097+ }
4098+ } else {
4099+ /* the rejection is handled, so the entry can be removed if present */
4100+ rp = find_rejected_promise (ctx , ts , promise );
4101+ if (rp ) {
4102+ JS_FreeValue (ctx , rp -> promise );
4103+ JS_FreeValue (ctx , rp -> reason );
4104+ list_del (& rp -> link );
4105+ free (rp );
4106+ }
4107+ }
4108+ }
4109+
4110+ /* check if there are pending promise rejections. It must be done
4111+ asynchrously in case a rejected promise is handled later. Currently
4112+ we do it once the application is about to sleep. It could be done
4113+ more often if needed. */
4114+ static void js_std_promise_rejection_check (JSContext * ctx )
4115+ {
4116+ JSRuntime * rt = JS_GetRuntime (ctx );
4117+ JSThreadState * ts = JS_GetRuntimeOpaque (rt );
4118+ struct list_head * el ;
4119+
4120+ if (unlikely (!list_empty (& ts -> rejected_promise_list ))) {
4121+ list_for_each (el , & ts -> rejected_promise_list ) {
4122+ JSRejectedPromiseEntry * rp = list_entry (el , JSRejectedPromiseEntry , link );
4123+ fprintf (stderr , "Possibly unhandled promise rejection: " );
4124+ js_std_dump_error1 (ctx , rp -> reason );
4125+ }
40584126 exit (1 );
40594127 }
40604128}
@@ -4077,6 +4145,8 @@ void js_std_loop(JSContext *ctx)
40774145 }
40784146 }
40794147
4148+ js_std_promise_rejection_check (ctx );
4149+
40804150 if (!os_poll_func || os_poll_func (ctx ))
40814151 break ;
40824152 }
@@ -4108,6 +4178,8 @@ JSValue js_std_await(JSContext *ctx, JSValue obj)
41084178 js_std_dump_error (ctx1 );
41094179 }
41104180 if (err == 0 ) {
4181+ js_std_promise_rejection_check (ctx );
4182+
41114183 if (os_poll_func )
41124184 os_poll_func (ctx );
41134185 }
0 commit comments