mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -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",
|
args: "run --check unhandled_rejection.ts",
|
||||||
output: "unhandled_rejection.ts.out",
|
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(exception) = tc_scope.exception() {
|
||||||
if let Some(js_uncaught_exception_cb) = js_uncaught_exception_cb {
|
if let Some(js_uncaught_exception_cb) = js_uncaught_exception_cb {
|
||||||
tc_scope.reset(); // Cancel pending exception.
|
tc_scope.reset(); // Cancel pending exception.
|
||||||
|
|
|
@ -1335,7 +1335,15 @@ impl JsRuntime {
|
||||||
.expect("Expected to get promise as module evaluation result");
|
.expect("Expected to get promise as module evaluation result");
|
||||||
let promise_global = v8::Global::new(tc_scope, promise);
|
let promise_global = v8::Global::new(tc_scope, promise);
|
||||||
let mut state = state_rc.borrow_mut();
|
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);
|
state.pending_promise_exceptions.remove(&promise_global);
|
||||||
|
}
|
||||||
|
}
|
||||||
let promise_global = v8::Global::new(tc_scope, promise);
|
let promise_global = v8::Global::new(tc_scope, promise);
|
||||||
state.pending_mod_evaluate.as_mut().unwrap().promise =
|
state.pending_mod_evaluate.as_mut().unwrap().promise =
|
||||||
Some(promise_global);
|
Some(promise_global);
|
||||||
|
|
|
@ -602,7 +602,7 @@ delete Intl.v8BreakIterator;
|
||||||
WeakMapPrototypeDelete(pendingRejectionsReasons, promise);
|
WeakMapPrototypeDelete(pendingRejectionsReasons, promise);
|
||||||
|
|
||||||
if (!hasPendingException) {
|
if (!hasPendingException) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = new PromiseRejectionEvent("unhandledrejection", {
|
const event = new PromiseRejectionEvent("unhandledrejection", {
|
||||||
|
@ -610,9 +610,21 @@ delete Intl.v8BreakIterator;
|
||||||
promise,
|
promise,
|
||||||
reason,
|
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) {
|
if (event.defaultPrevented) {
|
||||||
core.opSync("op_remove_pending_promise_exception", promise);
|
core.opSync("op_remove_pending_promise_exception", promise);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue