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.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use crate::colors;
|
||||||
use crate::fmt_errors::JsError;
|
use crate::fmt_errors::JsError;
|
||||||
use crate::ops::io::get_stdio;
|
use crate::ops::io::get_stdio;
|
||||||
use crate::permissions::Permissions;
|
use crate::permissions::Permissions;
|
||||||
|
@ -8,7 +9,9 @@ use crate::tokio_util::create_basic_runtime;
|
||||||
use crate::worker::WebWorker;
|
use crate::worker::WebWorker;
|
||||||
use crate::worker::WebWorkerHandle;
|
use crate::worker::WebWorkerHandle;
|
||||||
use crate::worker::WorkerEvent;
|
use crate::worker::WorkerEvent;
|
||||||
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::futures::channel::mpsc;
|
||||||
use deno_core::futures::future::FutureExt;
|
use deno_core::futures::future::FutureExt;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
|
@ -25,7 +28,15 @@ use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::JoinHandle;
|
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 op_state = rt.op_state();
|
||||||
let mut state = op_state.borrow_mut();
|
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_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_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)>;
|
pub type WorkersTable = HashMap<u32, (JoinHandle<()>, WebWorkerHandle)>;
|
||||||
|
@ -162,6 +188,12 @@ fn run_worker_thread(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
|
eprintln!(
|
||||||
|
"{}: Uncaught (in worker \"{}\") {}",
|
||||||
|
colors::red_bold("error"),
|
||||||
|
name,
|
||||||
|
e.to_string().trim_start_matches("Uncaught "),
|
||||||
|
);
|
||||||
sender
|
sender
|
||||||
.try_send(WorkerEvent::TerminalError(e))
|
.try_send(WorkerEvent::TerminalError(e))
|
||||||
.expect("Failed to post message to host");
|
.expect("Failed to post message to host");
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
((window) => {
|
((window) => {
|
||||||
const core = window.Deno.core;
|
const core = window.Deno.core;
|
||||||
|
const { Window } = window.__bootstrap.globalInterfaces;
|
||||||
const { log } = window.__bootstrap.util;
|
const { log } = window.__bootstrap.util;
|
||||||
|
|
||||||
function createWorker(
|
function createWorker(
|
||||||
|
@ -142,7 +143,14 @@
|
||||||
if (type === "terminalError") {
|
if (type === "terminalError") {
|
||||||
this.#terminated = true;
|
this.#terminated = true;
|
||||||
if (!this.#handleError(event.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;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +162,14 @@
|
||||||
|
|
||||||
if (type === "error") {
|
if (type === "error") {
|
||||||
if (!this.#handleError(event.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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ delete Object.prototype.__proto__;
|
||||||
((window) => {
|
((window) => {
|
||||||
const core = Deno.core;
|
const core = Deno.core;
|
||||||
const util = window.__bootstrap.util;
|
const util = window.__bootstrap.util;
|
||||||
const { illegalConstructorKey } = window.__bootstrap.webUtil;
|
|
||||||
const eventTarget = window.__bootstrap.eventTarget;
|
const eventTarget = window.__bootstrap.eventTarget;
|
||||||
|
const globalInterfaces = window.__bootstrap.globalInterfaces;
|
||||||
const dispatchMinimal = window.__bootstrap.dispatchMinimal;
|
const dispatchMinimal = window.__bootstrap.dispatchMinimal;
|
||||||
const build = window.__bootstrap.build;
|
const build = window.__bootstrap.build;
|
||||||
const version = window.__bootstrap.version;
|
const version = window.__bootstrap.version;
|
||||||
|
@ -192,42 +192,6 @@ delete Object.prototype.__proto__;
|
||||||
core.registerErrorClass("URIError", URIError);
|
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
|
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
|
||||||
const windowOrWorkerGlobalScope = {
|
const windowOrWorkerGlobalScope = {
|
||||||
Blob: util.nonEnumerable(fetch.Blob),
|
Blob: util.nonEnumerable(fetch.Blob),
|
||||||
|
@ -277,7 +241,7 @@ delete Object.prototype.__proto__;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mainRuntimeGlobalProperties = {
|
const mainRuntimeGlobalProperties = {
|
||||||
Window: util.nonEnumerable(Window),
|
Window: globalInterfaces.windowConstructorDescriptor,
|
||||||
window: util.readOnly(globalThis),
|
window: util.readOnly(globalThis),
|
||||||
self: util.readOnly(globalThis),
|
self: util.readOnly(globalThis),
|
||||||
// TODO(bartlomieju): from MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope)
|
// 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 = {
|
const workerRuntimeGlobalProperties = {
|
||||||
WorkerGlobalScope: util.nonEnumerable(WorkerGlobalScope),
|
WorkerGlobalScope: globalInterfaces.workerGlobalScopeConstructorDescriptor,
|
||||||
DedicatedWorkerGlobalScope: util.nonEnumerable(DedicatedWorkerGlobalScope),
|
DedicatedWorkerGlobalScope:
|
||||||
|
globalInterfaces.dedicatedWorkerGlobalScopeConstructorDescriptor,
|
||||||
self: util.readOnly(globalThis),
|
self: util.readOnly(globalThis),
|
||||||
onmessage: util.writable(onmessage),
|
onmessage: util.writable(onmessage),
|
||||||
onerror: util.writable(onerror),
|
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);
|
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 {
|
itest!(js_import_detect {
|
||||||
args: "run --quiet --reload js_import_detect.ts",
|
args: "run --quiet --reload js_import_detect.ts",
|
||||||
output: "js_import_detect.ts.out",
|
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.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use crate::colors;
|
||||||
use crate::fmt_errors::JsError;
|
use crate::fmt_errors::JsError;
|
||||||
use crate::inspector::DenoInspector;
|
use crate::inspector::DenoInspector;
|
||||||
use crate::inspector::InspectorSession;
|
use crate::inspector::InspectorSession;
|
||||||
|
@ -275,7 +276,7 @@ impl MainWorker {
|
||||||
ops::runtime::init(js_runtime, main_module);
|
ops::runtime::init(js_runtime, main_module);
|
||||||
ops::fetch::init(js_runtime, program_state.flags.ca_file.as_deref());
|
ops::fetch::init(js_runtime, program_state.flags.ca_file.as_deref());
|
||||||
ops::timers::init(js_runtime);
|
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::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_close", deno_core::op_close);
|
||||||
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
||||||
|
@ -443,11 +444,11 @@ impl WebWorker {
|
||||||
op_state.put::<Permissions>(permissions);
|
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::runtime::init(js_runtime, main_module);
|
||||||
ops::fetch::init(js_runtime, program_state.flags.ca_file.as_deref());
|
ops::fetch::init(js_runtime, program_state.flags.ca_file.as_deref());
|
||||||
ops::timers::init(js_runtime);
|
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_close", deno_core::op_close);
|
||||||
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
||||||
ops::reg_json_sync(
|
ops::reg_json_sync(
|
||||||
|
@ -510,6 +511,12 @@ impl WebWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = r {
|
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();
|
let mut sender = worker.internal_channels.sender.clone();
|
||||||
sender
|
sender
|
||||||
.try_send(WorkerEvent::Error(e))
|
.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",
|
"deno:op_crates/web/02_abort_signal.js",
|
||||||
include_str!("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",
|
"deno:op_crates/web/08_text_encoding.js",
|
||||||
include_str!("08_text_encoding.js"),
|
include_str!("08_text_encoding.js"),
|
||||||
|
|
Loading…
Reference in a new issue