@@ -18,7 +18,7 @@ static int timeout_interrupt_handler(JSRuntime *rt, void *opaque)
1818
1919static void sync_call (void )
2020{
21- const char * code =
21+ static const char code [] =
2222"(function() { \
2323 try { \
2424 while (true) {} \
@@ -43,7 +43,7 @@ static void sync_call(void)
4343
4444static void async_call (void )
4545{
46- const char * code =
46+ static const char code [] =
4747"(async function() { \
4848 const loop = async () => { \
4949 await Promise.resolve(); \
@@ -85,7 +85,7 @@ static JSValue save_value(JSContext *ctx, JSValueConst this_val,
8585
8686static void async_call_stack_overflow (void )
8787{
88- const char * code =
88+ static const char code [] =
8989"(async function() { \
9090 const f = () => f(); \
9191 try { \
@@ -199,7 +199,7 @@ static JSModuleDef *loader(JSContext *ctx, const char *name, void *opaque)
199199static void module_serde (void )
200200{
201201 JSRuntime * rt = JS_NewRuntime ();
202- JS_SetDumpFlags (rt , JS_DUMP_MODULE_RESOLVE );
202+ // JS_SetDumpFlags(rt, JS_DUMP_MODULE_RESOLVE);
203203 JS_SetModuleLoaderFunc (rt , NULL , loader , NULL );
204204 JSContext * ctx = JS_NewContext (rt );
205205 static const char code [] = "import {f} from 'b'; f()" ;
@@ -311,6 +311,169 @@ function addItem() { \
311311 JS_FreeRuntime (rt );
312312}
313313
314+ struct {
315+ int hook_type_call_count [4 ];
316+ } promise_hook_state ;
317+
318+ static void promise_hook_cb (JSContext * ctx , JSPromiseHookType type ,
319+ JSValueConst promise , JSValueConst parent_promise ,
320+ void * opaque )
321+ {
322+ assert (type == JS_PROMISE_HOOK_INIT ||
323+ type == JS_PROMISE_HOOK_BEFORE ||
324+ type == JS_PROMISE_HOOK_AFTER ||
325+ type == JS_PROMISE_HOOK_RESOLVE );
326+ promise_hook_state .hook_type_call_count [type ]++ ;
327+ assert (opaque == (void * )& promise_hook_state );
328+ if (!JS_IsUndefined (parent_promise )) {
329+ JSValue global_object = JS_GetGlobalObject (ctx );
330+ JS_SetPropertyStr (ctx , global_object , "actual" ,
331+ JS_DupValue (ctx , parent_promise ));
332+ JS_FreeValue (ctx , global_object );
333+ }
334+ }
335+
336+ static void promise_hook (void )
337+ {
338+ int * cc = promise_hook_state .hook_type_call_count ;
339+ JSContext * unused ;
340+ JSRuntime * rt = JS_NewRuntime ();
341+ //JS_SetDumpFlags(rt, JS_DUMP_PROMISE);
342+ JS_SetPromiseHook (rt , promise_hook_cb , & promise_hook_state );
343+ JSContext * ctx = JS_NewContext (rt );
344+ JSValue global_object = JS_GetGlobalObject (ctx );
345+ {
346+ // empty module; creates an outer and inner module promise;
347+ // JS_Eval returns the outer promise
348+ JSValue ret = JS_Eval (ctx , "" , 0 , "<input>" , JS_EVAL_TYPE_MODULE );
349+ assert (!JS_IsException (ret ));
350+ assert (JS_IsPromise (ret ));
351+ assert (JS_PROMISE_FULFILLED == JS_PromiseState (ctx , ret ));
352+ JS_FreeValue (ctx , ret );
353+ assert (2 == cc [JS_PROMISE_HOOK_INIT ]);
354+ assert (0 == cc [JS_PROMISE_HOOK_BEFORE ]);
355+ assert (0 == cc [JS_PROMISE_HOOK_AFTER ]);
356+ assert (2 == cc [JS_PROMISE_HOOK_RESOLVE ]);
357+ assert (!JS_IsJobPending (rt ));
358+ }
359+ memset (& promise_hook_state , 0 , sizeof (promise_hook_state ));
360+ {
361+ // module with unresolved promise; the outer and inner module promises
362+ // are resolved but not the user's promise
363+ static const char code [] = "new Promise(() => {})" ;
364+ JSValue ret = JS_Eval (ctx , code , strlen (code ), "<input>" , JS_EVAL_TYPE_MODULE );
365+ assert (!JS_IsException (ret ));
366+ assert (JS_IsPromise (ret ));
367+ assert (JS_PROMISE_FULFILLED == JS_PromiseState (ctx , ret )); // outer module promise
368+ JS_FreeValue (ctx , ret );
369+ assert (3 == cc [JS_PROMISE_HOOK_INIT ]);
370+ assert (0 == cc [JS_PROMISE_HOOK_BEFORE ]);
371+ assert (0 == cc [JS_PROMISE_HOOK_AFTER ]);
372+ assert (2 == cc [JS_PROMISE_HOOK_RESOLVE ]); // outer and inner module promise
373+ assert (!JS_IsJobPending (rt ));
374+ }
375+ memset (& promise_hook_state , 0 , sizeof (promise_hook_state ));
376+ {
377+ // module with resolved promise
378+ static const char code [] = "new Promise((resolve,reject) => resolve())" ;
379+ JSValue ret = JS_Eval (ctx , code , strlen (code ), "<input>" , JS_EVAL_TYPE_MODULE );
380+ assert (!JS_IsException (ret ));
381+ assert (JS_IsPromise (ret ));
382+ assert (JS_PROMISE_FULFILLED == JS_PromiseState (ctx , ret )); // outer module promise
383+ JS_FreeValue (ctx , ret );
384+ assert (3 == cc [JS_PROMISE_HOOK_INIT ]);
385+ assert (0 == cc [JS_PROMISE_HOOK_BEFORE ]);
386+ assert (0 == cc [JS_PROMISE_HOOK_AFTER ]);
387+ assert (3 == cc [JS_PROMISE_HOOK_RESOLVE ]);
388+ assert (!JS_IsJobPending (rt ));
389+ }
390+ memset (& promise_hook_state , 0 , sizeof (promise_hook_state ));
391+ {
392+ // module with rejected promise
393+ static const char code [] = "new Promise((resolve,reject) => reject())" ;
394+ JSValue ret = JS_Eval (ctx , code , strlen (code ), "<input>" , JS_EVAL_TYPE_MODULE );
395+ assert (!JS_IsException (ret ));
396+ assert (JS_IsPromise (ret ));
397+ assert (JS_PROMISE_FULFILLED == JS_PromiseState (ctx , ret )); // outer module promise
398+ JS_FreeValue (ctx , ret );
399+ assert (3 == cc [JS_PROMISE_HOOK_INIT ]);
400+ assert (0 == cc [JS_PROMISE_HOOK_BEFORE ]);
401+ assert (0 == cc [JS_PROMISE_HOOK_AFTER ]);
402+ assert (2 == cc [JS_PROMISE_HOOK_RESOLVE ]);
403+ assert (!JS_IsJobPending (rt ));
404+ }
405+ memset (& promise_hook_state , 0 , sizeof (promise_hook_state ));
406+ {
407+ // module with promise chain
408+ static const char code [] =
409+ "globalThis.count = 0;"
410+ "globalThis.actual = undefined;" // set by promise_hook_cb
411+ "globalThis.expected = new Promise(resolve => resolve());"
412+ "expected.then(_ => count++)" ;
413+ JSValue ret = JS_Eval (ctx , code , strlen (code ), "<input>" , JS_EVAL_TYPE_MODULE );
414+ assert (!JS_IsException (ret ));
415+ assert (JS_IsPromise (ret ));
416+ assert (JS_PROMISE_FULFILLED == JS_PromiseState (ctx , ret )); // outer module promise
417+ JS_FreeValue (ctx , ret );
418+ assert (4 == cc [JS_PROMISE_HOOK_INIT ]);
419+ assert (0 == cc [JS_PROMISE_HOOK_BEFORE ]);
420+ assert (0 == cc [JS_PROMISE_HOOK_AFTER ]);
421+ assert (3 == cc [JS_PROMISE_HOOK_RESOLVE ]);
422+ JSValue v = JS_GetPropertyStr (ctx , global_object , "count" );
423+ assert (!JS_IsException (v ));
424+ int32_t count ;
425+ assert (0 == JS_ToInt32 (ctx , & count , v ));
426+ assert (0 == count );
427+ JS_FreeValue (ctx , v );
428+ assert (JS_IsJobPending (rt ));
429+ assert (1 == JS_ExecutePendingJob (rt , & unused ));
430+ assert (!JS_HasException (ctx ));
431+ assert (4 == cc [JS_PROMISE_HOOK_INIT ]);
432+ assert (0 == cc [JS_PROMISE_HOOK_BEFORE ]);
433+ assert (0 == cc [JS_PROMISE_HOOK_AFTER ]);
434+ assert (4 == cc [JS_PROMISE_HOOK_RESOLVE ]);
435+ assert (!JS_IsJobPending (rt ));
436+ v = JS_GetPropertyStr (ctx , global_object , "count" );
437+ assert (!JS_IsException (v ));
438+ assert (0 == JS_ToInt32 (ctx , & count , v ));
439+ assert (1 == count );
440+ JS_FreeValue (ctx , v );
441+ JSValue actual = JS_GetPropertyStr (ctx , global_object , "actual" );
442+ JSValue expected = JS_GetPropertyStr (ctx , global_object , "expected" );
443+ assert (!JS_IsException (actual ));
444+ assert (!JS_IsException (expected ));
445+ assert (JS_IsSameValue (ctx , actual , expected ));
446+ JS_FreeValue (ctx , actual );
447+ JS_FreeValue (ctx , expected );
448+ }
449+ memset (& promise_hook_state , 0 , sizeof (promise_hook_state ));
450+ {
451+ // module with thenable; fires before and after hooks
452+ static const char code [] =
453+ "new Promise(resolve => resolve({then(resolve){ resolve() }}))" ;
454+ JSValue ret = JS_Eval (ctx , code , strlen (code ), "<input>" , JS_EVAL_TYPE_MODULE );
455+ assert (!JS_IsException (ret ));
456+ assert (JS_IsPromise (ret ));
457+ assert (JS_PROMISE_FULFILLED == JS_PromiseState (ctx , ret )); // outer module promise
458+ JS_FreeValue (ctx , ret );
459+ assert (3 == cc [JS_PROMISE_HOOK_INIT ]);
460+ assert (0 == cc [JS_PROMISE_HOOK_BEFORE ]);
461+ assert (0 == cc [JS_PROMISE_HOOK_AFTER ]);
462+ assert (2 == cc [JS_PROMISE_HOOK_RESOLVE ]);
463+ assert (JS_IsJobPending (rt ));
464+ assert (1 == JS_ExecutePendingJob (rt , & unused ));
465+ assert (!JS_HasException (ctx ));
466+ assert (3 == cc [JS_PROMISE_HOOK_INIT ]);
467+ assert (1 == cc [JS_PROMISE_HOOK_BEFORE ]);
468+ assert (1 == cc [JS_PROMISE_HOOK_AFTER ]);
469+ assert (3 == cc [JS_PROMISE_HOOK_RESOLVE ]);
470+ assert (!JS_IsJobPending (rt ));
471+ }
472+ JS_FreeValue (ctx , global_object );
473+ JS_FreeContext (ctx );
474+ JS_FreeRuntime (rt );
475+ }
476+
314477int main (void )
315478{
316479 sync_call ();
@@ -321,5 +484,6 @@ int main(void)
321484 module_serde ();
322485 two_byte_string ();
323486 weak_map_gc_check ();
487+ promise_hook ();
324488 return 0 ;
325489}
0 commit comments