1
0
Fork 0
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:
Bartek Iwańczuk 2022-07-23 00:40:42 +02:00 committed by GitHub
parent 72199303d8
commit 504d2936ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 43 additions and 4 deletions

View file

@ -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",
});

View 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!");

View 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

View file

@ -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.

View file

@ -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);

View file

@ -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);
} }