@@ -18,7 +18,7 @@ static int timeout_interrupt_handler(JSRuntime *rt, void *opaque)
18
18
19
19
static void sync_call (void )
20
20
{
21
- const char * code =
21
+ static const char code [] =
22
22
"(function() { \
23
23
try { \
24
24
while (true) {} \
@@ -43,7 +43,7 @@ static void sync_call(void)
43
43
44
44
static void async_call (void )
45
45
{
46
- const char * code =
46
+ static const char code [] =
47
47
"(async function() { \
48
48
const loop = async () => { \
49
49
await Promise.resolve(); \
@@ -85,7 +85,7 @@ static JSValue save_value(JSContext *ctx, JSValueConst this_val,
85
85
86
86
static void async_call_stack_overflow (void )
87
87
{
88
- const char * code =
88
+ static const char code [] =
89
89
"(async function() { \
90
90
const f = () => f(); \
91
91
try { \
@@ -199,7 +199,7 @@ static JSModuleDef *loader(JSContext *ctx, const char *name, void *opaque)
199
199
static void module_serde (void )
200
200
{
201
201
JSRuntime * rt = JS_NewRuntime ();
202
- JS_SetDumpFlags (rt , JS_DUMP_MODULE_RESOLVE );
202
+ // JS_SetDumpFlags(rt, JS_DUMP_MODULE_RESOLVE);
203
203
JS_SetModuleLoaderFunc (rt , NULL , loader , NULL );
204
204
JSContext * ctx = JS_NewContext (rt );
205
205
static const char code [] = "import {f} from 'b'; f()" ;
@@ -311,6 +311,169 @@ function addItem() { \
311
311
JS_FreeRuntime (rt );
312
312
}
313
313
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
+
314
477
int main (void )
315
478
{
316
479
sync_call ();
@@ -321,5 +484,6 @@ int main(void)
321
484
module_serde ();
322
485
two_byte_string ();
323
486
weak_map_gc_check ();
487
+ promise_hook ();
324
488
return 0 ;
325
489
}
0 commit comments