mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
fix(cli/worker): Print error stacks from the origin Worker (#7987)
Fixes #4728
This commit is contained in:
parent
57e95032c8
commit
7aba07cc77
12 changed files with 172 additions and 46 deletions
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::colors;
|
||||
use crate::fmt_errors::JsError;
|
||||
use crate::ops::io::get_stdio;
|
||||
use crate::permissions::Permissions;
|
||||
|
@ -8,7 +9,9 @@ use crate::tokio_util::create_basic_runtime;
|
|||
use crate::worker::WebWorker;
|
||||
use crate::worker::WebWorkerHandle;
|
||||
use crate::worker::WorkerEvent;
|
||||
use deno_core::error::generic_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::channel::mpsc;
|
||||
use deno_core::futures::future::FutureExt;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
|
@ -25,7 +28,15 @@ use std::rc::Rc;
|
|||
use std::sync::Arc;
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
pub fn init(rt: &mut deno_core::JsRuntime) {
|
||||
#[derive(Deserialize)]
|
||||
struct HostUnhandledErrorArgs {
|
||||
message: String,
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
rt: &mut deno_core::JsRuntime,
|
||||
sender: Option<mpsc::Sender<WorkerEvent>>,
|
||||
) {
|
||||
{
|
||||
let op_state = rt.op_state();
|
||||
let mut state = op_state.borrow_mut();
|
||||
|
@ -40,6 +51,21 @@ pub fn init(rt: &mut deno_core::JsRuntime) {
|
|||
);
|
||||
super::reg_json_sync(rt, "op_host_post_message", op_host_post_message);
|
||||
super::reg_json_async(rt, "op_host_get_message", op_host_get_message);
|
||||
super::reg_json_sync(
|
||||
rt,
|
||||
"op_host_unhandled_error",
|
||||
move |_state, args, _zero_copy| {
|
||||
if let Some(mut sender) = sender.clone() {
|
||||
let args: HostUnhandledErrorArgs = serde_json::from_value(args)?;
|
||||
sender
|
||||
.try_send(WorkerEvent::Error(generic_error(args.message)))
|
||||
.expect("Failed to propagate error event to parent worker");
|
||||
Ok(json!(true))
|
||||
} else {
|
||||
Err(generic_error("Cannot be called from main worker."))
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub type WorkersTable = HashMap<u32, (JoinHandle<()>, WebWorkerHandle)>;
|
||||
|
@ -162,6 +188,12 @@ fn run_worker_thread(
|
|||
}
|
||||
|
||||
if let Err(e) = result {
|
||||
eprintln!(
|
||||
"{}: Uncaught (in worker \"{}\") {}",
|
||||
colors::red_bold("error"),
|
||||
name,
|
||||
e.to_string().trim_start_matches("Uncaught "),
|
||||
);
|
||||
sender
|
||||
.try_send(WorkerEvent::TerminalError(e))
|
||||
.expect("Failed to post message to host");
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const { Window } = window.__bootstrap.globalInterfaces;
|
||||
const { log } = window.__bootstrap.util;
|
||||
|
||||
function createWorker(
|
||||
|
@ -142,7 +143,14 @@
|
|||
if (type === "terminalError") {
|
||||
this.#terminated = true;
|
||||
if (!this.#handleError(event.error)) {
|
||||
throw Error(event.error.message);
|
||||
if (globalThis instanceof Window) {
|
||||
throw new Error("Unhandled error event reached main worker.");
|
||||
} else {
|
||||
core.jsonOpSync(
|
||||
"op_host_unhandled_error",
|
||||
{ message: event.error.message },
|
||||
);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -154,7 +162,14 @@
|
|||
|
||||
if (type === "error") {
|
||||
if (!this.#handleError(event.error)) {
|
||||
throw Error(event.error.message);
|
||||
if (globalThis instanceof Window) {
|
||||
throw new Error("Unhandled error event reached main worker.");
|
||||
} else {
|
||||
core.jsonOpSync(
|
||||
"op_host_unhandled_error",
|
||||
{ message: event.error.message },
|
||||
);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ delete Object.prototype.__proto__;
|
|||
((window) => {
|
||||
const core = Deno.core;
|
||||
const util = window.__bootstrap.util;
|
||||
const { illegalConstructorKey } = window.__bootstrap.webUtil;
|
||||
const eventTarget = window.__bootstrap.eventTarget;
|
||||
const globalInterfaces = window.__bootstrap.globalInterfaces;
|
||||
const dispatchMinimal = window.__bootstrap.dispatchMinimal;
|
||||
const build = window.__bootstrap.build;
|
||||
const version = window.__bootstrap.version;
|
||||
|
@ -192,42 +192,6 @@ delete Object.prototype.__proto__;
|
|||
core.registerErrorClass("URIError", URIError);
|
||||
}
|
||||
|
||||
class Window extends EventTarget {
|
||||
constructor(key) {
|
||||
if (key !== illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "Window";
|
||||
}
|
||||
}
|
||||
|
||||
class WorkerGlobalScope extends EventTarget {
|
||||
constructor(key) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "WorkerGlobalScope";
|
||||
}
|
||||
}
|
||||
|
||||
class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
|
||||
constructor(key) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "DedicatedWorkerGlobalScope";
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
|
||||
const windowOrWorkerGlobalScope = {
|
||||
Blob: util.nonEnumerable(fetch.Blob),
|
||||
|
@ -277,7 +241,7 @@ delete Object.prototype.__proto__;
|
|||
};
|
||||
|
||||
const mainRuntimeGlobalProperties = {
|
||||
Window: util.nonEnumerable(Window),
|
||||
Window: globalInterfaces.windowConstructorDescriptor,
|
||||
window: util.readOnly(globalThis),
|
||||
self: util.readOnly(globalThis),
|
||||
// TODO(bartlomieju): from MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope)
|
||||
|
@ -292,8 +256,9 @@ delete Object.prototype.__proto__;
|
|||
};
|
||||
|
||||
const workerRuntimeGlobalProperties = {
|
||||
WorkerGlobalScope: util.nonEnumerable(WorkerGlobalScope),
|
||||
DedicatedWorkerGlobalScope: util.nonEnumerable(DedicatedWorkerGlobalScope),
|
||||
WorkerGlobalScope: globalInterfaces.workerGlobalScopeConstructorDescriptor,
|
||||
DedicatedWorkerGlobalScope:
|
||||
globalInterfaces.dedicatedWorkerGlobalScopeConstructorDescriptor,
|
||||
self: util.readOnly(globalThis),
|
||||
onmessage: util.writable(onmessage),
|
||||
onerror: util.writable(onerror),
|
||||
|
|
5
cli/tests/073_worker_error.ts
Normal file
5
cli/tests/073_worker_error.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
const worker = new Worker(
|
||||
new URL("subdir/worker_error.ts", import.meta.url).href,
|
||||
{ type: "module", name: "bar" },
|
||||
);
|
||||
setTimeout(() => worker.terminate(), 30000);
|
5
cli/tests/073_worker_error.ts.out
Normal file
5
cli/tests/073_worker_error.ts.out
Normal file
|
@ -0,0 +1,5 @@
|
|||
[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD]
|
||||
at foo ([WILDCARD])
|
||||
at [WILDCARD]
|
||||
error: Uncaught Error: Unhandled error event reached main worker.
|
||||
at Worker.#poll ([WILDCARD])
|
5
cli/tests/074_worker_nested_error.ts
Normal file
5
cli/tests/074_worker_nested_error.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
const worker = new Worker(
|
||||
new URL("073_worker_error.ts", import.meta.url).href,
|
||||
{ type: "module", name: "baz" },
|
||||
);
|
||||
setTimeout(() => worker.terminate(), 30000);
|
5
cli/tests/074_worker_nested_error.ts.out
Normal file
5
cli/tests/074_worker_nested_error.ts.out
Normal file
|
@ -0,0 +1,5 @@
|
|||
[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD]
|
||||
at foo ([WILDCARD])
|
||||
at [WILDCARD]
|
||||
error: Uncaught Error: Unhandled error event reached main worker.
|
||||
at Worker.#poll ([WILDCARD])
|
|
@ -2136,6 +2136,18 @@ fn _066_prompt() {
|
|||
util::test_pty(args, output, input);
|
||||
}
|
||||
|
||||
itest!(_073_worker_error {
|
||||
args: "run -A 073_worker_error.ts",
|
||||
output: "073_worker_error.ts.out",
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(_074_worker_nested_error {
|
||||
args: "run -A 074_worker_nested_error.ts",
|
||||
output: "074_worker_nested_error.ts.out",
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(js_import_detect {
|
||||
args: "run --quiet --reload js_import_detect.ts",
|
||||
output: "js_import_detect.ts.out",
|
||||
|
|
5
cli/tests/subdir/worker_error.ts
Normal file
5
cli/tests/subdir/worker_error.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
function foo() {
|
||||
throw new Error("foo");
|
||||
}
|
||||
|
||||
foo();
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::colors;
|
||||
use crate::fmt_errors::JsError;
|
||||
use crate::inspector::DenoInspector;
|
||||
use crate::inspector::InspectorSession;
|
||||
|
@ -275,7 +276,7 @@ impl MainWorker {
|
|||
ops::runtime::init(js_runtime, main_module);
|
||||
ops::fetch::init(js_runtime, program_state.flags.ca_file.as_deref());
|
||||
ops::timers::init(js_runtime);
|
||||
ops::worker_host::init(js_runtime);
|
||||
ops::worker_host::init(js_runtime, None);
|
||||
ops::random::init(js_runtime, program_state.flags.seed);
|
||||
ops::reg_json_sync(js_runtime, "op_close", deno_core::op_close);
|
||||
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
||||
|
@ -443,11 +444,11 @@ impl WebWorker {
|
|||
op_state.put::<Permissions>(permissions);
|
||||
}
|
||||
|
||||
ops::web_worker::init(js_runtime, sender, handle);
|
||||
ops::web_worker::init(js_runtime, sender.clone(), handle);
|
||||
ops::runtime::init(js_runtime, main_module);
|
||||
ops::fetch::init(js_runtime, program_state.flags.ca_file.as_deref());
|
||||
ops::timers::init(js_runtime);
|
||||
ops::worker_host::init(js_runtime);
|
||||
ops::worker_host::init(js_runtime, Some(sender));
|
||||
ops::reg_json_sync(js_runtime, "op_close", deno_core::op_close);
|
||||
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
||||
ops::reg_json_sync(
|
||||
|
@ -510,6 +511,12 @@ impl WebWorker {
|
|||
}
|
||||
|
||||
if let Err(e) = r {
|
||||
eprintln!(
|
||||
"{}: Uncaught (in worker \"{}\") {}",
|
||||
colors::red_bold("error"),
|
||||
worker.name.to_string(),
|
||||
e.to_string().trim_start_matches("Uncaught "),
|
||||
);
|
||||
let mut sender = worker.internal_channels.sender.clone();
|
||||
sender
|
||||
.try_send(WorkerEvent::Error(e))
|
||||
|
|
66
op_crates/web/03_global_interfaces.js
Normal file
66
op_crates/web/03_global_interfaces.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
((window) => {
|
||||
const { EventTarget } = window;
|
||||
|
||||
const illegalConstructorKey = Symbol("illegalConstuctorKey");
|
||||
|
||||
class Window extends EventTarget {
|
||||
constructor(key) {
|
||||
if (key !== illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "Window";
|
||||
}
|
||||
}
|
||||
|
||||
class WorkerGlobalScope extends EventTarget {
|
||||
constructor(key) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "WorkerGlobalScope";
|
||||
}
|
||||
}
|
||||
|
||||
class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
|
||||
constructor(key) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "DedicatedWorkerGlobalScope";
|
||||
}
|
||||
}
|
||||
|
||||
window.__bootstrap = (window.__bootstrap || {});
|
||||
window.__bootstrap.globalInterfaces = {
|
||||
DedicatedWorkerGlobalScope,
|
||||
Window,
|
||||
WorkerGlobalScope,
|
||||
dedicatedWorkerGlobalScopeConstructorDescriptor: {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: DedicatedWorkerGlobalScope,
|
||||
writable: true,
|
||||
},
|
||||
windowConstructorDescriptor: {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: Window,
|
||||
writable: true,
|
||||
},
|
||||
workerGlobalScopeConstructorDescriptor: {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: WorkerGlobalScope,
|
||||
writable: true,
|
||||
},
|
||||
};
|
||||
})(this);
|
|
@ -52,6 +52,10 @@ pub fn init(isolate: &mut JsRuntime) {
|
|||
"deno:op_crates/web/02_abort_signal.js",
|
||||
include_str!("02_abort_signal.js"),
|
||||
),
|
||||
(
|
||||
"deno:op_crates/web/03_global_interfaces.js",
|
||||
include_str!("03_global_interfaces.js"),
|
||||
),
|
||||
(
|
||||
"deno:op_crates/web/08_text_encoding.js",
|
||||
include_str!("08_text_encoding.js"),
|
||||
|
|
Loading…
Reference in a new issue