summaryrefslogtreecommitdiff
path: root/runtime/js
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2022-07-20 20:28:19 +0200
committerGitHub <noreply@github.com>2022-07-20 20:28:19 +0200
commitd53936eb7d3fe4cda8e06f7310e4c8f12702b413 (patch)
treead4b5cf09dee34e8618096c35e58b54c0312f2e1 /runtime/js
parent6e350b2b7c7b027b31694875cdb423f494d1b423 (diff)
Reland "feat: add "unhandledrejection" event support" (#15211)
Diffstat (limited to 'runtime/js')
-rw-r--r--runtime/js/99_main.js75
1 files changed, 75 insertions, 0 deletions
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index c13faa936..3421528d2 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -11,6 +11,10 @@ delete Intl.v8BreakIterator;
((window) => {
const core = Deno.core;
const {
+ ArrayPrototypeIndexOf,
+ ArrayPrototypePush,
+ ArrayPrototypeShift,
+ ArrayPrototypeSplice,
ArrayPrototypeMap,
DateNow,
Error,
@@ -27,7 +31,11 @@ delete Intl.v8BreakIterator;
SymbolFor,
SymbolIterator,
PromisePrototypeThen,
+ SafeWeakMap,
TypeError,
+ WeakMapPrototypeDelete,
+ WeakMapPrototypeGet,
+ WeakMapPrototypeSet,
} = window.__bootstrap.primordials;
const util = window.__bootstrap.util;
const eventTarget = window.__bootstrap.eventTarget;
@@ -233,6 +241,7 @@ delete Intl.v8BreakIterator;
function runtimeStart(runtimeOptions, source) {
core.setMacrotaskCallback(timers.handleTimerMacrotask);
+ core.setMacrotaskCallback(promiseRejectMacrotaskCallback);
core.setWasmStreamingCallback(fetch.handleWasmStreaming);
core.opSync("op_set_format_exception_callback", formatException);
version.setVersions(
@@ -411,6 +420,7 @@ delete Intl.v8BreakIterator;
PerformanceEntry: util.nonEnumerable(performance.PerformanceEntry),
PerformanceMark: util.nonEnumerable(performance.PerformanceMark),
PerformanceMeasure: util.nonEnumerable(performance.PerformanceMeasure),
+ PromiseRejectionEvent: util.nonEnumerable(PromiseRejectionEvent),
ProgressEvent: util.nonEnumerable(ProgressEvent),
ReadableStream: util.nonEnumerable(streams.ReadableStream),
ReadableStreamDefaultReader: util.nonEnumerable(
@@ -553,6 +563,63 @@ delete Intl.v8BreakIterator;
postMessage: util.writable(postMessage),
};
+ const pendingRejections = [];
+ const pendingRejectionsReasons = new SafeWeakMap();
+
+ function promiseRejectCallback(type, promise, reason) {
+ switch (type) {
+ case 0: {
+ core.opSync("op_store_pending_promise_exception", promise, reason);
+ ArrayPrototypePush(pendingRejections, promise);
+ WeakMapPrototypeSet(pendingRejectionsReasons, promise, reason);
+ break;
+ }
+ case 1: {
+ core.opSync("op_remove_pending_promise_exception", promise);
+ const index = ArrayPrototypeIndexOf(pendingRejections, promise);
+ if (index > -1) {
+ ArrayPrototypeSplice(pendingRejections, index, 1);
+ WeakMapPrototypeDelete(pendingRejectionsReasons, promise);
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return !!globalThis.onunhandledrejection ||
+ eventTarget.listenerCount(globalThis, "unhandledrejection") > 0;
+ }
+
+ function promiseRejectMacrotaskCallback() {
+ while (pendingRejections.length > 0) {
+ const promise = ArrayPrototypeShift(pendingRejections);
+ const hasPendingException = core.opSync(
+ "op_has_pending_promise_exception",
+ promise,
+ );
+ const reason = WeakMapPrototypeGet(pendingRejectionsReasons, promise);
+ WeakMapPrototypeDelete(pendingRejectionsReasons, promise);
+
+ if (!hasPendingException) {
+ return;
+ }
+
+ const event = new PromiseRejectionEvent("unhandledrejection", {
+ cancelable: true,
+ promise,
+ reason,
+ });
+ globalThis.dispatchEvent(event);
+
+ // If event was not prevented we will let Rust side handle it.
+ if (event.defaultPrevented) {
+ core.opSync("op_remove_pending_promise_exception", promise);
+ }
+ }
+ return true;
+ }
+
let hasBootstrapped = false;
function bootstrapMainRuntime(runtimeOptions) {
@@ -585,6 +652,10 @@ delete Intl.v8BreakIterator;
defineEventHandler(window, "load");
defineEventHandler(window, "beforeunload");
defineEventHandler(window, "unload");
+ defineEventHandler(window, "unhandledrejection");
+
+ core.setPromiseRejectCallback(promiseRejectCallback);
+
const isUnloadDispatched = SymbolFor("isUnloadDispatched");
// Stores the flag for checking whether unload is dispatched or not.
// This prevents the recursive dispatches of unload events.
@@ -685,6 +756,10 @@ delete Intl.v8BreakIterator;
defineEventHandler(self, "message");
defineEventHandler(self, "error", undefined, true);
+ defineEventHandler(self, "unhandledrejection");
+
+ core.setPromiseRejectCallback(promiseRejectCallback);
+
// `Deno.exit()` is an alias to `self.close()`. Setting and exit
// code using an op in worker context is a no-op.
os.setExitHandler((_exitCode) => {