mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
fix: unhandledrejection handling for sync throw in top level (#15279)
Fixes an edge in "unhandledrejection" event that prevent synchronous errors being surfaced when throw from a top-level scope.
This commit is contained in:
parent
72199303d8
commit
504d2936ec
6 changed files with 43 additions and 4 deletions
|
@ -2787,3 +2787,8 @@ itest!(unhandled_rejection {
|
|||
args: "run --check unhandled_rejection.ts",
|
||||
output: "unhandled_rejection.ts.out",
|
||||
});
|
||||
|
||||
itest!(unhandled_rejection_sync_error {
|
||||
args: "run --check unhandled_rejection_sync_error.ts",
|
||||
output: "unhandled_rejection_sync_error.ts.out",
|
||||
});
|
||||
|
|
6
cli/tests/testdata/unhandled_rejection_sync_error.ts
vendored
Normal file
6
cli/tests/testdata/unhandled_rejection_sync_error.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
globalThis.addEventListener("unhandledrejection", (e) => {
|
||||
console.log("unhandled rejection at:", e.promise, "reason:", e.reason);
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
throw new Error("boom!");
|
6
cli/tests/testdata/unhandled_rejection_sync_error.ts.out
vendored
Normal file
6
cli/tests/testdata/unhandled_rejection_sync_error.ts.out
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
[WILDCARD]
|
||||
unhandled rejection at: Promise {
|
||||
<rejected> Error: boom!
|
||||
at file:///[WILDCARD]testdata/unhandled_rejection_sync_error.ts:6:7
|
||||
} reason: Error: boom!
|
||||
at file:///[WILDCARD]testdata/unhandled_rejection_sync_error.ts:6:7
|
|
@ -363,6 +363,8 @@ pub extern "C" fn promise_reject_callback(message: v8::PromiseRejectMessage) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): remove this whole block, `js_uncaught_exception_cb` is
|
||||
// not needed anymore
|
||||
if let Some(exception) = tc_scope.exception() {
|
||||
if let Some(js_uncaught_exception_cb) = js_uncaught_exception_cb {
|
||||
tc_scope.reset(); // Cancel pending exception.
|
||||
|
|
|
@ -1335,7 +1335,15 @@ impl JsRuntime {
|
|||
.expect("Expected to get promise as module evaluation result");
|
||||
let promise_global = v8::Global::new(tc_scope, promise);
|
||||
let mut state = state_rc.borrow_mut();
|
||||
{
|
||||
let pending_mod_evaluate = state.pending_mod_evaluate.as_ref().unwrap();
|
||||
let pending_rejection_was_already_handled = pending_mod_evaluate
|
||||
.handled_promise_rejections
|
||||
.contains(&promise_global);
|
||||
if !pending_rejection_was_already_handled {
|
||||
state.pending_promise_exceptions.remove(&promise_global);
|
||||
}
|
||||
}
|
||||
let promise_global = v8::Global::new(tc_scope, promise);
|
||||
state.pending_mod_evaluate.as_mut().unwrap().promise =
|
||||
Some(promise_global);
|
||||
|
|
|
@ -602,7 +602,7 @@ delete Intl.v8BreakIterator;
|
|||
WeakMapPrototypeDelete(pendingRejectionsReasons, promise);
|
||||
|
||||
if (!hasPendingException) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
const event = new PromiseRejectionEvent("unhandledrejection", {
|
||||
|
@ -610,9 +610,21 @@ delete Intl.v8BreakIterator;
|
|||
promise,
|
||||
reason,
|
||||
});
|
||||
globalThis.dispatchEvent(event);
|
||||
|
||||
// If event was not prevented we will let Rust side handle it.
|
||||
const errorEventCb = (event) => {
|
||||
if (event.error === reason) {
|
||||
core.opSync("op_remove_pending_promise_exception", promise);
|
||||
}
|
||||
};
|
||||
// Add a callback for "error" event - it will be dispatched
|
||||
// if error is thrown during dispatch of "unhandledrejection"
|
||||
// event.
|
||||
globalThis.addEventListener("error", errorEventCb);
|
||||
globalThis.dispatchEvent(event);
|
||||
globalThis.removeEventListener("error", errorEventCb);
|
||||
|
||||
// If event was not prevented (or "unhandledrejection" listeners didn't
|
||||
// throw) we will let Rust side handle it.
|
||||
if (event.defaultPrevented) {
|
||||
core.opSync("op_remove_pending_promise_exception", promise);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue