Skip to content

Commit

Permalink
feat(ext/web): Add error events for event listener and timer errors (d…
Browse files Browse the repository at this point in the history
…enoland#14159)

- feat: Add handleable error event for even listener errors
- feat: Add handleable error event for setTimeout()/setInterval() errors
- feat: Add Deno.core.destructureError()
- feat: Add Deno.core.terminate()
- fix: Don't throw listener errors from dispatchEvent()
- fix: Use biased mode when selecting between mod_evaluate() and
  run_event_loop() results
  • Loading branch information
nayeemrmn committed Apr 13, 2022
1 parent d621ce1 commit 4d18f55
Show file tree
Hide file tree
Showing 35 changed files with 440 additions and 131 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ jobs:
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
key: 5-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }}
key: 7-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }}

# In main branch, always creates fresh cache
- name: Cache build output (main)
Expand All @@ -252,7 +252,7 @@ jobs:
!./target/*/*.zip
!./target/*/*.tar.gz
key: |
5-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }}
7-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }}
# Restore cache from the latest 'main' branch build.
- name: Cache build output (PR)
Expand All @@ -268,7 +268,7 @@ jobs:
!./target/*/*.tar.gz
key: never_saved
restore-keys: |
5-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-
7-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-
# Don't save cache after building PRs or branches other than 'main'.
- name: Skip save cache (PR)
Expand Down
55 changes: 51 additions & 4 deletions cli/dts/lib.deno.window.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
/// <reference lib="deno.webstorage" />
/// <reference lib="esnext" />

interface WindowEventMap {
"error": ErrorEvent;
}

declare class Window extends EventTarget {
new(): Window;
readonly window: Window & typeof globalThis;
readonly self: Window & typeof globalThis;
onerror: ((this: Window, ev: ErrorEvent) => any) | null;
onload: ((this: Window, ev: Event) => any) | null;
onunload: ((this: Window, ev: Event) => any) | null;
close: () => void;
Expand All @@ -25,10 +30,38 @@ declare class Window extends EventTarget {
location: Location;
localStorage: Storage;
sessionStorage: Storage;

addEventListener<K extends keyof WindowEventMap>(
type: K,
listener: (
this: Window,
ev: WindowEventMap[K],
) => any,
options?: boolean | AddEventListenerOptions,
): void;
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
): void;
removeEventListener<K extends keyof WindowEventMap>(
type: K,
listener: (
this: Window,
ev: WindowEventMap[K],
) => any,
options?: boolean | EventListenerOptions,
): void;
removeEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions,
): void;
}

declare var window: Window & typeof globalThis;
declare var self: Window & typeof globalThis;
declare var onerror: ((this: Window, ev: ErrorEvent) => any) | null;
declare var onload: ((this: Window, ev: Event) => any) | null;
declare var onunload: ((this: Window, ev: Event) => any) | null;
declare var localStorage: Storage;
Expand Down Expand Up @@ -77,10 +110,17 @@ declare function prompt(message?: string, defaultValue?: string): string | null;
* dispatchEvent(new Event('unload'));
* ```
*/
declare function addEventListener<
K extends keyof WindowEventMap,
>(
type: K,
listener: (this: Window, ev: WindowEventMap[K]) => any,
options?: boolean | AddEventListenerOptions,
): void;
declare function addEventListener(
type: string,
callback: EventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions | undefined,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
): void;

/** Remove a previously registered event listener from the global scope
Expand All @@ -91,10 +131,17 @@ declare function addEventListener(
* removeEventListener('load', listener);
* ```
*/
declare function removeEventListener<
K extends keyof WindowEventMap,
>(
type: K,
listener: (this: Window, ev: WindowEventMap[K]) => any,
options?: boolean | EventListenerOptions,
): void;
declare function removeEventListener(
type: string,
callback: EventListenerOrEventListenerObject | null,
options?: boolean | EventListenerOptions | undefined,
listener: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions,
): void;

// TODO(nayeemrmn): Move this to `extensions/web` where its implementation is.
Expand Down
29 changes: 29 additions & 0 deletions cli/tests/integration/run_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2685,3 +2685,32 @@ itest!(future_check2 {
output: "future_check2.out",
envs: vec![("DENO_FUTURE_CHECK".to_string(), "1".to_string())],
});

itest!(event_listener_error {
args: "run --quiet event_listener_error.ts",
output: "event_listener_error.ts.out",
exit_code: 1,
});

itest!(event_listener_error_handled {
args: "run --quiet event_listener_error_handled.ts",
output: "event_listener_error_handled.ts.out",
});

// https://github.com/denoland/deno/pull/14159#issuecomment-1092285446
itest!(event_listener_error_immediate_exit {
args: "run --quiet event_listener_error_immediate_exit.ts",
output: "event_listener_error_immediate_exit.ts.out",
exit_code: 1,
});

itest!(set_timeout_error {
args: "run --quiet set_timeout_error.ts",
output: "set_timeout_error.ts.out",
exit_code: 1,
});

itest!(set_timeout_error_handled {
args: "run --quiet set_timeout_error_handled.ts",
output: "set_timeout_error_handled.ts.out",
});
6 changes: 6 additions & 0 deletions cli/tests/testdata/event_listener_error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
addEventListener("foo", () => {
throw new Error("bar");
});
console.log(1);
dispatchEvent(new CustomEvent("foo"));
console.log(2);
7 changes: 7 additions & 0 deletions cli/tests/testdata/event_listener_error.ts.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
1
error: Uncaught Error: bar
throw new Error("bar");
^
at [WILDCARD]/event_listener_error.ts:2:9
at [WILDCARD]
at [WILDCARD]/event_listener_error.ts:5:1
23 changes: 23 additions & 0 deletions cli/tests/testdata/event_listener_error_handled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
addEventListener("error", (event) => {
console.log({
cancelable: event.cancelable,
message: event.message,
filename: event.filename?.slice?.(-100),
lineno: event.lineno,
colno: event.colno,
error: event.error,
});
event.preventDefault();
});

onerror = (event) => {
console.log("onerror() called", event.error);
};

addEventListener("foo", () => {
throw new Error("bar");
});

console.log(1);
dispatchEvent(new CustomEvent("foo"));
console.log(2);
17 changes: 17 additions & 0 deletions cli/tests/testdata/event_listener_error_handled.ts.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
1
{
cancelable: true,
message: "Uncaught Error: bar",
filename: "[WILDCARD]/event_listener_error_handled.ts",
lineno: 18,
colno: 9,
error: Error: bar
at [WILDCARD]/event_listener_error_handled.ts:18:9
at [WILDCARD]
at [WILDCARD]/event_listener_error_handled.ts:22:1
}
onerror() called Error: bar
at [WILDCARD]/event_listener_error_handled.ts:18:9
at [WILDCARD]
at [WILDCARD]/event_listener_error_handled.ts:22:1
2
12 changes: 12 additions & 0 deletions cli/tests/testdata/event_listener_error_immediate_exit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
addEventListener("foo", () => {
queueMicrotask(() => console.log("queueMicrotask"));
setTimeout(() => console.log("timer"), 0);
throw new Error("bar");
});
console.log(1);
// @ts-ignore Deno.core
Deno.core.setNextTickCallback(() => console.log("nextTick"));
// @ts-ignore Deno.core
Deno.core.setHasTickScheduled(true);
dispatchEvent(new CustomEvent("foo"));
console.log(2);
6 changes: 6 additions & 0 deletions cli/tests/testdata/event_listener_error_immediate_exit.ts.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
1
error: Uncaught Error: bar
throw new Error("bar");
^
at [WILDCARD]/event_listener_error_immediate_exit.ts:4:9[WILDCARD]
at [WILDCARD]/event_listener_error_immediate_exit.ts:11:1
3 changes: 3 additions & 0 deletions cli/tests/testdata/set_timeout_error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
setTimeout(() => {
throw new Error("foo");
}, 0);
5 changes: 5 additions & 0 deletions cli/tests/testdata/set_timeout_error.ts.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: Uncaught Error: foo
throw new Error("foo");
^
at [WILDCARD]/set_timeout_error.ts:2:9
at [WILDCARD]
19 changes: 19 additions & 0 deletions cli/tests/testdata/set_timeout_error_handled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
addEventListener("error", (event) => {
console.log({
cancelable: event.cancelable,
message: event.message,
filename: event.filename?.slice?.(-100),
lineno: event.lineno,
colno: event.colno,
error: event.error,
});
event.preventDefault();
});

onerror = (event) => {
console.log("onerror() called", event.error);
};

setTimeout(() => {
throw new Error("foo");
}, 0);
13 changes: 13 additions & 0 deletions cli/tests/testdata/set_timeout_error_handled.ts.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
cancelable: true,
message: "Uncaught Error: foo",
filename: "[WILDCARD]/set_timeout_error_handled.ts",
lineno: 18,
colno: 9,
error: Error: foo
at [WILDCARD]/set_timeout_error_handled.ts:18:9
at [WILDCARD]
}
onerror() called Error: foo
at [WILDCARD]/set_timeout_error_handled.ts:18:9
at [WILDCARD]
2 changes: 1 addition & 1 deletion cli/tests/testdata/worker_drop_handle_race.js.out
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ error: Uncaught (in worker "") Error
at [WILDCARD]/workers/drop_handle_race.js:2:9
at Object.action (deno:ext/web/02_timers.js:[WILDCARD])
at handleTimerMacrotask (deno:ext/web/02_timers.js:[WILDCARD])
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl (deno:runtime/js/11_workers.js:[WILDCARD])
8 changes: 4 additions & 4 deletions cli/tests/testdata/worker_event_handler_test.js.out
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Target from self.onmessage: [object DedicatedWorkerGlobalScope]
Target from message event listener: [object DedicatedWorkerGlobalScope]
Arguments from self.onerror: [
"Some error message",
"",
0,
0,
"Uncaught Error: Some error message",
"[WILDCARD]/worker_event_handlers.js",
9,
9,
Error: Some error message
at [WILDCARD]
]
Expand Down
2 changes: 1 addition & 1 deletion cli/tests/testdata/workers/nonexistent_worker.out
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[WILDCARD]error: Uncaught (in worker "") Module not found "file:https:///[WILDCARD]/workers/doesnt_exist.js".
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
2 changes: 1 addition & 1 deletion cli/tests/testdata/workers/permissions_blob_local.ts.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
at blob:null/[WILDCARD]:1:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
2 changes: 1 addition & 1 deletion cli/tests/testdata/workers/permissions_blob_remote.ts.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
at blob:null/[WILDCARD]:1:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
2 changes: 1 addition & 1 deletion cli/tests/testdata/workers/permissions_data_local.ts.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
at data:application/javascript;base64,[WILDCARD]:1:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
2 changes: 1 addition & 1 deletion cli/tests/testdata/workers/permissions_data_remote.ts.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ error: Uncaught (in worker "") (in promise) TypeError: Requires net access to "e
await import("https://example.com/some/file.ts");
^
at async http:https://localhost:4545/workers/dynamic_remote.ts:2:1
[WILDCARD]error: Uncaught (in promise) Error: Unhandled error event in child worker.
[WILDCARD]error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
at http:https://localhost:4545/workers/static_remote.ts:2:8
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
2 changes: 1 addition & 1 deletion cli/tests/testdata/workers/worker_async_error.ts.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ error: Uncaught (in worker "foo") (in promise) Error: bar
^
at [WILDCARD]/async_error.ts:[WILDCARD]
at [WILDCARD]/async_error.ts:[WILDCARD]
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
2 changes: 1 addition & 1 deletion cli/tests/testdata/workers/worker_error.ts.out
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD]
at foo ([WILDCARD])
at [WILDCARD]
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
error: Uncaught (in worker "foo") (in promise) Error: bar
error: Uncaught (in worker "foo") Error: bar
throw new Error("bar");
^
at onmessage ([WILDCARD]/message_handler_error.ts:[WILDCARD])
at [WILDCARD]
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
4 changes: 2 additions & 2 deletions cli/tests/testdata/workers/worker_nested_error.ts.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
^
at foo ([WILDCARD]/workers/error.ts:[WILDCARD])
at [WILDCARD]/workers/error.ts:[WILDCARD]
error: Uncaught (in worker "baz") (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in worker "baz") (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
error: Uncaught (in promise) Error: Unhandled error event in child worker.
error: Uncaught (in promise) Error: Unhandled error in child worker.
at Worker.#pollControl ([WILDCARD])
Loading

0 comments on commit 4d18f55

Please sign in to comment.