Skip to content

Fix reporting handled promises as unhandled in tracker #1038

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -50210,6 +50210,32 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc,
return res2;
}

static JSValue promise_rejection_tracker_job(JSContext *ctx, int argc,
JSValueConst *argv)
{
JSRuntime *rt;
JSPromiseData *s;
JSValueConst promise;

assert(argc == 1);

rt = ctx->rt;
promise = argv[0];
s = JS_GetOpaque(promise, JS_CLASS_PROMISE);

if (!s || s->promise_state != JS_PROMISE_REJECTED)
return JS_UNDEFINED; /* should never happen */

promise_trace(ctx, "promise_rejection_tracker_job\n");

// Check again in case the hook was removed.
if (rt->host_promise_rejection_tracker)
rt->host_promise_rejection_tracker(
ctx, promise, s->promise_result, s->is_handled, rt->host_promise_rejection_tracker_opaque);

return JS_UNDEFINED;
}

void JS_SetPromiseHook(JSRuntime *rt, JSPromiseHook promise_hook, void *opaque)
{
rt->promise_hook = promise_hook;
Expand Down Expand Up @@ -50247,14 +50273,6 @@ static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
}
}

if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
JSRuntime *rt = ctx->rt;
if (rt->host_promise_rejection_tracker) {
rt->host_promise_rejection_tracker(ctx, promise, value, false,
rt->host_promise_rejection_tracker_opaque);
}
}

list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
rd = list_entry(el, JSPromiseReactionData, link);
args[0] = rd->resolving_funcs[0];
Expand All @@ -50272,6 +50290,12 @@ static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
list_del(&rd->link);
promise_reaction_data_free(ctx->rt, rd);
}

if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
JSRuntime *rt = ctx->rt;
if (rt->host_promise_rejection_tracker)
JS_EnqueueJob(ctx, promise_rejection_tracker_job, 1, &promise);
}
}

static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
Expand Down Expand Up @@ -51018,10 +51042,8 @@ static __exception int perform_promise_then(JSContext *ctx,
JSValueConst args[5];
if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
JSRuntime *rt = ctx->rt;
if (rt->host_promise_rejection_tracker) {
rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
true, rt->host_promise_rejection_tracker_opaque);
}
if (rt->host_promise_rejection_tracker)
JS_EnqueueJob(ctx, promise_rejection_tracker_job, 1, &promise);
}
i = s->promise_state - JS_PROMISE_FULFILLED;
rd = rd_array[i];
Expand Down
15 changes: 12 additions & 3 deletions run-test262.c
Original file line number Diff line number Diff line change
Expand Up @@ -1716,7 +1716,8 @@ JSContext *JS_NewCustomContext(JSRuntime *rt)
int run_test_buf(ThreadLocalStorage *tls, const char *filename, char *harness,
namelist_t *ip, char *buf, size_t buf_len,
const char* error_type, int eval_flags, bool is_negative,
bool is_async, bool can_block, int *msec)
bool is_async, bool can_block, bool track_promise_rejections,
int *msec)
{
JSRuntime *rt;
JSContext *ctx;
Expand All @@ -1741,6 +1742,9 @@ int run_test_buf(ThreadLocalStorage *tls, const char *filename, char *harness,
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, (void *) filename);

if (track_promise_rejections)
JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, NULL);

add_helpers(ctx);

for (i = 0; i < ip->count; i++) {
Expand Down Expand Up @@ -1787,6 +1791,7 @@ int run_test(ThreadLocalStorage *tls, const char *filename, int *msec)
int ret, eval_flags, use_strict, use_nostrict;
bool is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip;
bool detect_module = true;
bool track_promise_rejections = false;
bool can_block;
namelist_t include_list = { 0 }, *ip = &include_list;

Expand Down Expand Up @@ -1845,6 +1850,9 @@ int run_test(ThreadLocalStorage *tls, const char *filename, int *msec)
else if (str_equal(option, "qjs:no-detect-module")) {
detect_module = false;
}
else if (str_equal(option, "qjs:track-promise-rejections")) {
track_promise_rejections = true;
}
else if (str_equal(option, "module")) {
is_module = true;
skip |= skip_module;
Expand Down Expand Up @@ -1939,12 +1947,13 @@ int run_test(ThreadLocalStorage *tls, const char *filename, int *msec)
if (use_nostrict) {
ret = run_test_buf(tls, filename, harness, ip, buf, buf_len,
error_type, eval_flags, is_negative, is_async,
can_block, msec);
can_block, track_promise_rejections, msec);
}
if (use_strict) {
ret |= run_test_buf(tls, filename, harness, ip, buf, buf_len,
error_type, eval_flags | JS_EVAL_FLAG_STRICT,
is_negative, is_async, can_block, msec);
is_negative, is_async, can_block,
track_promise_rejections, msec);
}
}
namelist_free(&include_list);
Expand Down
6 changes: 6 additions & 0 deletions tests/bug39.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*---
flags: [qjs:track-promise-rejections]
---*/

Promise.reject().catch(() => print('oops'))
Promise.resolve().then(() => print('ok'))