From 68fd7a927b26c3e72cf73515821450f3aa252014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 22 Sep 2020 23:30:03 +0200 Subject: [PATCH] refactor(core): support error stack, remove js_check (#7629) This commit adds support for stack traces in "deno_core". Implementation of "Display" trait for "JsError" has been updated and in consequence "deno_core::js_check" became obsolete and removed. --- cli/build.rs | 11 +- cli/js.rs | 20 +- cli/source_maps.rs | 2 + core/error.rs | 46 ++-- core/examples/http_bench_bin_ops.rs | 3 +- core/examples/http_bench_json_ops.rs | 3 +- core/modules.rs | 9 +- core/runtime.rs | 322 +++++++++++++++++++-------- op_crates/fetch/lib.rs | 11 +- op_crates/web/lib.rs | 49 ++-- 10 files changed, 309 insertions(+), 167 deletions(-) diff --git a/cli/build.rs b/cli/build.rs index d969a3415b..64cef30829 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -2,7 +2,6 @@ mod op_fetch_asset; -use deno_core::js_check; use deno_core::JsRuntime; use deno_core::RuntimeOptions; use std::collections::HashMap; @@ -24,10 +23,12 @@ fn create_snapshot( println!("cargo:rerun-if-changed={}", file.display()); let display_path = file.strip_prefix(display_root).unwrap(); let display_path_str = display_path.display().to_string(); - js_check(isolate.execute( - &("deno:".to_string() + &display_path_str.replace('\\', "/")), - &std::fs::read_to_string(&file).unwrap(), - )); + isolate + .execute( + &("deno:".to_string() + &display_path_str.replace('\\', "/")), + &std::fs::read_to_string(&file).unwrap(), + ) + .unwrap(); } let snapshot = isolate.snapshot(); diff --git a/cli/js.rs b/cli/js.rs index ef6d6c7e8b..beb8df1923 100644 --- a/cli/js.rs +++ b/cli/js.rs @@ -34,15 +34,17 @@ fn cli_snapshot() { startup_snapshot: Some(deno_isolate_init()), ..Default::default() }); - deno_core::js_check(isolate.execute( - "", - r#" + isolate + .execute( + "", + r#" if (!(bootstrap.mainRuntime && bootstrap.workerRuntime)) { throw Error("bad"); } console.log("we have console.log!!!"); "#, - )); + ) + .unwrap(); } #[test] @@ -51,13 +53,15 @@ fn compiler_snapshot() { startup_snapshot: Some(compiler_isolate_init()), ..Default::default() }); - deno_core::js_check(isolate.execute( - "", - r#" + isolate + .execute( + "", + r#" if (!(bootstrapCompilerRuntime)) { throw Error("bad"); } console.log(`ts version: ${ts.version}`); "#, - )); + ) + .unwrap(); } diff --git a/cli/source_maps.rs b/cli/source_maps.rs index 1e7a14a0ec..4744482a77 100644 --- a/cli/source_maps.rs +++ b/cli/source_maps.rs @@ -78,6 +78,7 @@ pub fn apply_source_map( start_column, end_column, frames: js_error.frames.clone(), + stack: None, } } @@ -204,6 +205,7 @@ mod tests { start_column: Some(16), end_column: None, frames: vec![], + stack: None, }; let getter = MockSourceMapGetter {}; let actual = apply_source_map(&e, &getter); diff --git a/core/error.rs b/core/error.rs index 25a7ec12bf..1aa67e26c2 100644 --- a/core/error.rs +++ b/core/error.rs @@ -97,6 +97,7 @@ pub struct JsError { pub start_column: Option, // 0-based pub end_column: Option, // 0-based pub frames: Vec, + pub stack: Option, } #[derive(Debug, PartialEq, Clone)] @@ -166,7 +167,7 @@ impl JsError { let msg = v8::Exception::create_message(scope, exception); - let (message, frames) = if exception.is_native_error() { + let (message, frames, stack) = if exception.is_native_error() { // The exception is a JS Error object. let exception: v8::Local = exception.clone().try_into().unwrap(); @@ -184,7 +185,14 @@ impl JsError { // Access error.stack to ensure that prepareStackTrace() has been called. // This should populate error.__callSiteEvals. - let _ = get_property(scope, exception, "stack"); + let stack: Option> = + get_property(scope, exception, "stack") + .unwrap() + .try_into() + .ok(); + let stack = stack.map(|s| s.to_rust_string_lossy(scope)); + + // FIXME(bartlmieju): the rest of this function is CLI only // Read an array of structured frames from error.__callSiteEvals. let frames_v8 = get_property(scope, exception, "__callSiteEvals"); @@ -300,12 +308,12 @@ impl JsError { }); } } - (message, frames) + (message, frames, stack) } else { // The exception is not a JS Error object. // Get the message given by V8::Exception::create_message(), and provide // empty frames. - (msg.get(scope).to_rust_string_lossy(scope), vec![]) + (msg.get(scope).to_rust_string_lossy(scope), vec![], None) }; Self { @@ -321,6 +329,7 @@ impl JsError { start_column: msg.get_start_column().try_into().ok(), end_column: msg.get_end_column().try_into().ok(), frames, + stack, } } } @@ -339,35 +348,24 @@ fn format_source_loc( impl Display for JsError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { + if let Some(stack) = &self.stack { + let stack_lines = stack.lines(); + if stack_lines.count() > 1 { + return writeln!(f, "{}", stack); + } + } + + writeln!(f, "{}", self.message)?; if let Some(script_resource_name) = &self.script_resource_name { if self.line_number.is_some() && self.start_column.is_some() { - assert!(self.line_number.is_some()); - assert!(self.start_column.is_some()); let source_loc = format_source_loc( script_resource_name, self.line_number.unwrap(), self.start_column.unwrap(), ); - write!(f, "{}", source_loc)?; - } - if self.source_line.is_some() { - let source_line = self.source_line.as_ref().unwrap(); - write!(f, "\n{}\n", source_line)?; - let mut s = String::new(); - for i in 0..self.end_column.unwrap() { - if i >= self.start_column.unwrap() { - s.push('^'); - } else if source_line.chars().nth(i as usize).unwrap() == '\t' { - s.push('\t'); - } else { - s.push(' '); - } - } - writeln!(f, "{}", s)?; + writeln!(f, " at {}", source_loc)?; } } - - write!(f, "{}", self.message)?; Ok(()) } } diff --git a/core/examples/http_bench_bin_ops.rs b/core/examples/http_bench_bin_ops.rs index 513ac5b3ab..bc92007b07 100644 --- a/core/examples/http_bench_bin_ops.rs +++ b/core/examples/http_bench_bin_ops.rs @@ -3,7 +3,6 @@ #[macro_use] extern crate log; -use deno_core::js_check; use deno_core::BufVec; use deno_core::JsRuntime; use deno_core::Op; @@ -263,7 +262,7 @@ fn main() { .unwrap(); isolate.await }; - js_check(runtime.block_on(future)); + runtime.block_on(future).unwrap(); } #[test] diff --git a/core/examples/http_bench_json_ops.rs b/core/examples/http_bench_json_ops.rs index fab222f4c8..a260159e58 100644 --- a/core/examples/http_bench_json_ops.rs +++ b/core/examples/http_bench_json_ops.rs @@ -5,7 +5,6 @@ extern crate log; use deno_core::error::bad_resource_id; use deno_core::error::AnyError; -use deno_core::js_check; use deno_core::BufVec; use deno_core::JsRuntime; use deno_core::OpState; @@ -196,5 +195,5 @@ fn main() { .unwrap(); isolate.await }; - js_check(runtime.block_on(future)); + runtime.block_on(future).unwrap(); } diff --git a/core/modules.rs b/core/modules.rs index 414423be2f..294bcfb051 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -466,7 +466,6 @@ impl Modules { #[cfg(test)] mod tests { use super::*; - use crate::js_check; use crate::JsRuntime; use crate::RuntimeOptions; use futures::future::FutureExt; @@ -654,7 +653,7 @@ mod tests { let a_id_fut = runtime.load_module(&spec, None); let a_id = futures::executor::block_on(a_id_fut).expect("Failed to load"); - js_check(runtime.mod_evaluate(a_id)); + runtime.mod_evaluate(a_id).unwrap(); let l = loads.lock().unwrap(); assert_eq!( l.to_vec(), @@ -721,7 +720,7 @@ mod tests { let result = runtime.load_module(&spec, None).await; assert!(result.is_ok()); let circular1_id = result.unwrap(); - js_check(runtime.mod_evaluate(circular1_id)); + runtime.mod_evaluate(circular1_id).unwrap(); let l = loads.lock().unwrap(); assert_eq!( @@ -798,7 +797,7 @@ mod tests { println!(">> result {:?}", result); assert!(result.is_ok()); let redirect1_id = result.unwrap(); - js_check(runtime.mod_evaluate(redirect1_id)); + runtime.mod_evaluate(redirect1_id).unwrap(); let l = loads.lock().unwrap(); assert_eq!( l.to_vec(), @@ -948,7 +947,7 @@ mod tests { let main_id = futures::executor::block_on(main_id_fut).expect("Failed to load"); - js_check(runtime.mod_evaluate(main_id)); + runtime.mod_evaluate(main_id).unwrap(); let l = loads.lock().unwrap(); assert_eq!( diff --git a/core/runtime.rs b/core/runtime.rs index 9c076efd12..2a6d9f3ee8 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -332,7 +332,7 @@ impl JsRuntime { pub(crate) fn shared_init(&mut self) { if self.needs_init { self.needs_init = false; - js_check(self.execute("core.js", include_str!("core.js"))); + self.execute("core.js", include_str!("core.js")).unwrap(); } } @@ -1359,16 +1359,18 @@ pub mod tests { runtime.register_op("test", dispatch); - js_check(runtime.execute( - "setup.js", - r#" + runtime + .execute( + "setup.js", + r#" function assert(cond) { if (!cond) { throw Error("assert"); } } "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); (runtime, dispatch_count) } @@ -1376,9 +1378,10 @@ pub mod tests { #[test] fn test_dispatch() { let (mut runtime, dispatch_count) = setup(Mode::Async); - js_check(runtime.execute( - "filename.js", - r#" + runtime + .execute( + "filename.js", + r#" let control = new Uint8Array([42]); Deno.core.send(1, control); async function main() { @@ -1386,40 +1389,45 @@ pub mod tests { } main(); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); } #[test] fn test_dispatch_no_zero_copy_buf() { let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(0)); - js_check(runtime.execute( - "filename.js", - r#" + runtime + .execute( + "filename.js", + r#" Deno.core.send(1); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); } #[test] fn test_dispatch_stack_zero_copy_bufs() { let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(2)); - js_check(runtime.execute( - "filename.js", - r#" + runtime + .execute( + "filename.js", + r#" let zero_copy_a = new Uint8Array([0]); let zero_copy_b = new Uint8Array([1]); Deno.core.send(1, zero_copy_a, zero_copy_b); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); } #[test] fn test_dispatch_heap_zero_copy_bufs() { let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(5)); - js_check(runtime.execute( + runtime.execute( "filename.js", r#" let zero_copy_a = new Uint8Array([0]); @@ -1429,7 +1437,7 @@ pub mod tests { let zero_copy_e = new Uint8Array([4]); Deno.core.send(1, zero_copy_a, zero_copy_b, zero_copy_c, zero_copy_d, zero_copy_e); "#, - )); + ).unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); } @@ -1438,39 +1446,45 @@ pub mod tests { run_in_task(|cx| { let (mut runtime, dispatch_count) = setup(Mode::Async); - js_check(runtime.execute( - "setup2.js", - r#" + runtime + .execute( + "setup2.js", + r#" let nrecv = 0; Deno.core.setAsyncHandler(1, (buf) => { nrecv++; }); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); - js_check(runtime.execute( - "check1.js", - r#" + runtime + .execute( + "check1.js", + r#" assert(nrecv == 0); let control = new Uint8Array([42]); Deno.core.send(1, control); assert(nrecv == 0); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); - js_check(runtime.execute( - "check2.js", - r#" + runtime + .execute( + "check2.js", + r#" assert(nrecv == 1); Deno.core.send(1, control); assert(nrecv == 1); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); - js_check(runtime.execute("check3.js", "assert(nrecv == 2)")); + runtime.execute("check3.js", "assert(nrecv == 2)").unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); // We are idle, so the next poll should be the last. assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); @@ -1481,9 +1495,10 @@ pub mod tests { fn test_poll_async_optional_ops() { run_in_task(|cx| { let (mut runtime, dispatch_count) = setup(Mode::AsyncUnref); - js_check(runtime.execute( - "check1.js", - r#" + runtime + .execute( + "check1.js", + r#" Deno.core.setAsyncHandler(1, (buf) => { // This handler will never be called assert(false); @@ -1491,7 +1506,8 @@ pub mod tests { let control = new Uint8Array([42]); Deno.core.send(1, control); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); // The above op never finish, but runtime can finish // because the op is an unreffed async op. @@ -1520,7 +1536,7 @@ pub mod tests { match isolate.execute("infinite_loop.js", "for(;;) {}") { Ok(_) => panic!("execution should be terminated"), Err(e) => { - assert_eq!(e.to_string(), "Uncaught Error: execution terminated") + assert_eq!(e.to_string(), "Uncaught Error: execution terminated\n") } }; @@ -1561,9 +1577,10 @@ pub mod tests { #[test] fn overflow_req_sync() { let (mut runtime, dispatch_count) = setup(Mode::OverflowReqSync); - js_check(runtime.execute( - "overflow_req_sync.js", - r#" + runtime + .execute( + "overflow_req_sync.js", + r#" let asyncRecv = 0; Deno.core.setAsyncHandler(1, (buf) => { asyncRecv++ }); // Large message that will overflow the shared space. @@ -1574,7 +1591,8 @@ pub mod tests { assert(response[0] == 43); assert(asyncRecv == 0); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); } @@ -1583,9 +1601,10 @@ pub mod tests { // TODO(ry) This test is quite slow due to memcpy-ing 100MB into JS. We // should optimize this. let (mut runtime, dispatch_count) = setup(Mode::OverflowResSync); - js_check(runtime.execute( - "overflow_res_sync.js", - r#" + runtime + .execute( + "overflow_res_sync.js", + r#" let asyncRecv = 0; Deno.core.setAsyncHandler(1, (buf) => { asyncRecv++ }); // Large message that will overflow the shared space. @@ -1596,7 +1615,8 @@ pub mod tests { assert(response[0] == 99); assert(asyncRecv == 0); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); } @@ -1604,9 +1624,10 @@ pub mod tests { fn overflow_req_async() { run_in_task(|cx| { let (mut runtime, dispatch_count) = setup(Mode::OverflowReqAsync); - js_check(runtime.execute( - "overflow_req_async.js", - r#" + runtime + .execute( + "overflow_req_async.js", + r#" let asyncRecv = 0; Deno.core.setAsyncHandler(1, (buf) => { assert(buf.byteLength === 1); @@ -1620,10 +1641,13 @@ pub mod tests { assert(response == null); assert(asyncRecv == 0); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); - js_check(runtime.execute("check.js", "assert(asyncRecv == 1);")); + runtime + .execute("check.js", "assert(asyncRecv == 1);") + .unwrap(); }); } @@ -1633,9 +1657,10 @@ pub mod tests { // TODO(ry) This test is quite slow due to memcpy-ing 100MB into JS. We // should optimize this. let (mut runtime, dispatch_count) = setup(Mode::OverflowResAsync); - js_check(runtime.execute( - "overflow_res_async.js", - r#" + runtime + .execute( + "overflow_res_async.js", + r#" let asyncRecv = 0; Deno.core.setAsyncHandler(1, (buf) => { assert(buf.byteLength === 100 * 1024 * 1024); @@ -1648,10 +1673,13 @@ pub mod tests { assert(response == null); assert(asyncRecv == 0); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); poll_until_ready(&mut runtime, 3).unwrap(); - js_check(runtime.execute("check.js", "assert(asyncRecv == 1);")); + runtime + .execute("check.js", "assert(asyncRecv == 1);") + .unwrap(); }); } @@ -1661,9 +1689,10 @@ pub mod tests { // should optimize this. run_in_task(|_cx| { let (mut runtime, dispatch_count) = setup(Mode::OverflowResAsync); - js_check(runtime.execute( - "overflow_res_multiple_dispatch_async.js", - r#" + runtime + .execute( + "overflow_res_multiple_dispatch_async.js", + r#" let asyncRecv = 0; Deno.core.setAsyncHandler(1, (buf) => { assert(buf.byteLength === 100 * 1024 * 1024); @@ -1679,10 +1708,13 @@ pub mod tests { // are done even if shared space overflows Deno.core.dispatch(1, control); "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); poll_until_ready(&mut runtime, 3).unwrap(); - js_check(runtime.execute("check.js", "assert(asyncRecv == 2);")); + runtime + .execute("check.js", "assert(asyncRecv == 2);") + .unwrap(); }); } @@ -1690,9 +1722,10 @@ pub mod tests { fn test_pre_dispatch() { run_in_task(|mut cx| { let (mut runtime, _dispatch_count) = setup(Mode::OverflowResAsync); - js_check(runtime.execute( - "bad_op_id.js", - r#" + runtime + .execute( + "bad_op_id.js", + r#" let thrown; try { Deno.core.dispatch(100); @@ -1701,7 +1734,8 @@ pub mod tests { } assert(String(thrown) === "TypeError: Unknown op id: 100"); "#, - )); + ) + .unwrap(); if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { unreachable!(); } @@ -1712,7 +1746,9 @@ pub mod tests { fn core_test_js() { run_in_task(|mut cx| { let (mut runtime, _dispatch_count) = setup(Mode::Async); - js_check(runtime.execute("core_test.js", include_str!("core_test.js"))); + runtime + .execute("core_test.js", include_str!("core_test.js")) + .unwrap(); if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { unreachable!(); } @@ -1733,10 +1769,12 @@ pub mod tests { fn test_encode_decode() { run_in_task(|mut cx| { let (mut runtime, _dispatch_count) = setup(Mode::Async); - js_check(runtime.execute( - "encode_decode_test.js", - include_str!("encode_decode_test.js"), - )); + runtime + .execute( + "encode_decode_test.js", + include_str!("encode_decode_test.js"), + ) + .unwrap(); if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { unreachable!(); } @@ -1750,7 +1788,7 @@ pub mod tests { will_snapshot: true, ..Default::default() }); - js_check(runtime.execute("a.js", "a = 1 + 2")); + runtime.execute("a.js", "a = 1 + 2").unwrap(); runtime.snapshot() }; @@ -1759,7 +1797,9 @@ pub mod tests { startup_snapshot: Some(snapshot), ..Default::default() }); - js_check(runtime2.execute("check.js", "if (a != 3) throw Error('x')")); + runtime2 + .execute("check.js", "if (a != 3) throw Error('x')") + .unwrap(); } #[test] @@ -1769,7 +1809,7 @@ pub mod tests { will_snapshot: true, ..Default::default() }); - js_check(runtime.execute("a.js", "a = 1 + 2")); + runtime.execute("a.js", "a = 1 + 2").unwrap(); let snap: &[u8] = &*runtime.snapshot(); Vec::from(snap).into_boxed_slice() }; @@ -1779,7 +1819,9 @@ pub mod tests { startup_snapshot: Some(snapshot), ..Default::default() }); - js_check(runtime2.execute("check.js", "if (a != 3) throw Error('x')")); + runtime2 + .execute("check.js", "if (a != 3) throw Error('x')") + .unwrap(); } #[test] @@ -1926,16 +1968,18 @@ pub mod tests { }); runtime.register_op("test", dispatcher); - js_check(runtime.execute( - "setup.js", - r#" + runtime + .execute( + "setup.js", + r#" function assert(cond) { if (!cond) { throw Error("assert"); } } "#, - )); + ) + .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); @@ -1972,14 +2016,14 @@ pub mod tests { assert_eq!(imports.len(), 0); } - js_check(runtime.mod_instantiate(mod_b)); + runtime.mod_instantiate(mod_b).unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); assert_eq!(resolve_count.load(Ordering::SeqCst), 1); - js_check(runtime.mod_instantiate(mod_a)); + runtime.mod_instantiate(mod_a).unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 0); - js_check(runtime.mod_evaluate(mod_a)); + runtime.mod_evaluate(mod_a).unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); } @@ -2024,14 +2068,16 @@ pub mod tests { ..Default::default() }); - js_check(runtime.execute( - "file:///dyn_import2.js", - r#" + runtime + .execute( + "file:///dyn_import2.js", + r#" (async () => { await import("/foo.js"); })(); "#, - )); + ) + .unwrap(); assert_eq!(count.load(Ordering::Relaxed), 0); // We should get an error here. @@ -2107,9 +2153,10 @@ pub mod tests { }); // Dynamically import mod_b - js_check(runtime.execute( - "file:///dyn_import3.js", - r#" + runtime + .execute( + "file:///dyn_import3.js", + r#" (async () => { let mod = await import("./b.js"); if (mod.b() !== 'b') { @@ -2122,7 +2169,8 @@ pub mod tests { } })(); "#, - )); + ) + .unwrap(); // First poll runs `prepare_load` hook. assert!(matches!(runtime.poll_unpin(cx), Poll::Pending)); @@ -2148,9 +2196,10 @@ pub mod tests { module_loader: Some(loader), ..Default::default() }); - js_check(runtime.execute( - "file:///dyn_import3.js", - r#" + runtime + .execute( + "file:///dyn_import3.js", + r#" (async () => { let mod = await import("./b.js"); if (mod.b() !== 'b') { @@ -2160,7 +2209,8 @@ pub mod tests { Deno.core.ops(); })(); "#, - )); + ) + .unwrap(); // First poll runs `prepare_load` hook. let _ = runtime.poll_unpin(cx); assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1); @@ -2213,8 +2263,94 @@ pub mod tests { ) .unwrap(); - js_check(runtime.mod_evaluate(module_id)); + runtime.mod_evaluate(module_id).unwrap(); let _snapshot = runtime.snapshot(); } + + #[test] + fn test_error_without_stack() { + let mut runtime = JsRuntime::new(RuntimeOptions::default()); + // SyntaxError + let result = runtime.execute( + "error_without_stack.js", + r#" +function main() { + console.log("asdf); +} + +main(); +"#, + ); + let expected_error = r#"Uncaught SyntaxError: Invalid or unexpected token + at error_without_stack.js:3:14 +"#; + assert_eq!(result.unwrap_err().to_string(), expected_error); + } + + #[test] + fn test_error_stack() { + let mut runtime = JsRuntime::new(RuntimeOptions::default()); + let result = runtime.execute( + "error_stack.js", + r#" +function assert(cond) { + if (!cond) { + throw Error("assert"); + } +} + +function main() { + assert(false); +} + +main(); + "#, + ); + let expected_error = r#"Error: assert + at assert (error_stack.js:4:11) + at main (error_stack.js:9:3) + at error_stack.js:12:1 +"#; + assert_eq!(result.unwrap_err().to_string(), expected_error); + } + + #[test] + fn test_error_async_stack() { + run_in_task(|cx| { + let mut runtime = JsRuntime::new(RuntimeOptions::default()); + runtime + .execute( + "error_async_stack.js", + r#" +(async () => { + const p = (async () => { + await Promise.resolve().then(() => { + throw new Error("async"); + }); + })(); + + try { + await p; + } catch (error) { + console.log(error.stack); + throw error; + } +})();"#, + ) + .unwrap(); + let expected_error = r#"Error: async + at error_async_stack.js:5:13 + at async error_async_stack.js:4:5 + at async error_async_stack.js:10:5 +"#; + + match runtime.poll_unpin(cx) { + Poll::Ready(Err(e)) => { + assert_eq!(e.to_string(), expected_error); + } + _ => panic!(), + }; + }) + } } diff --git a/op_crates/fetch/lib.rs b/op_crates/fetch/lib.rs index 3692f916cc..5a314f7529 100644 --- a/op_crates/fetch/lib.rs +++ b/op_crates/fetch/lib.rs @@ -6,7 +6,6 @@ use deno_core::error::bad_resource_id; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::futures; -use deno_core::js_check; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; @@ -50,10 +49,12 @@ pub fn init(isolate: &mut JsRuntime) { println!("cargo:rerun-if-changed={}", file.display()); let display_path = file.strip_prefix(display_root).unwrap(); let display_path_str = display_path.display().to_string(); - js_check(isolate.execute( - &("deno:".to_string() + &display_path_str.replace('\\', "/")), - &std::fs::read_to_string(&file).unwrap(), - )); + isolate + .execute( + &("deno:".to_string() + &display_path_str.replace('\\', "/")), + &std::fs::read_to_string(&file).unwrap(), + ) + .unwrap(); } } diff --git a/op_crates/web/lib.rs b/op_crates/web/lib.rs index 278accd2f1..1078a7917e 100644 --- a/op_crates/web/lib.rs +++ b/op_crates/web/lib.rs @@ -2,7 +2,6 @@ use deno_core::error::uri_error; use deno_core::error::AnyError; -use deno_core::js_check; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; @@ -55,10 +54,12 @@ pub fn init(isolate: &mut JsRuntime) { println!("cargo:rerun-if-changed={}", file.display()); let display_path = file.strip_prefix(display_root).unwrap(); let display_path_str = display_path.display().to_string(); - js_check(isolate.execute( - &("deno:".to_string() + &display_path_str.replace('\\', "/")), - &std::fs::read_to_string(&file).unwrap(), - )); + isolate + .execute( + &("deno:".to_string() + &display_path_str.replace('\\', "/")), + &std::fs::read_to_string(&file).unwrap(), + ) + .unwrap(); } } @@ -68,7 +69,6 @@ pub fn get_declaration() -> PathBuf { #[cfg(test)] mod tests { - use deno_core::js_check; use deno_core::JsRuntime; use futures::future::lazy; use futures::future::FutureExt; @@ -92,10 +92,12 @@ mod tests { fn test_abort_controller() { run_in_task(|mut cx| { let mut isolate = setup(); - js_check(isolate.execute( - "abort_controller_test.js", - include_str!("abort_controller_test.js"), - )); + isolate + .execute( + "abort_controller_test.js", + include_str!("abort_controller_test.js"), + ) + .unwrap(); if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { unreachable!(); } @@ -106,7 +108,9 @@ mod tests { fn test_event() { run_in_task(|mut cx| { let mut isolate = setup(); - js_check(isolate.execute("event_test.js", include_str!("event_test.js"))); + isolate + .execute("event_test.js", include_str!("event_test.js")) + .unwrap(); if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { unreachable!(); } @@ -121,8 +125,8 @@ mod tests { if let Err(error) = result { let error_string = error.to_string(); // Test that the script specifier is a URL: `deno:`. - assert!(error_string.starts_with("deno:op_crates/web/01_event.js")); - assert!(error_string.contains("Uncaught TypeError")); + assert!(error_string.contains("deno:op_crates/web/01_event.js")); + assert!(error_string.contains("TypeError")); } else { unreachable!(); } @@ -136,12 +140,9 @@ mod tests { fn test_event_target() { run_in_task(|mut cx| { let mut isolate = setup(); - js_check( - isolate.execute( - "event_target_test.js", - include_str!("event_target_test.js"), - ), - ); + isolate + .execute("event_target_test.js", include_str!("event_target_test.js")) + .unwrap(); if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { unreachable!(); } @@ -152,10 +153,12 @@ mod tests { fn test_text_encoding() { run_in_task(|mut cx| { let mut isolate = setup(); - js_check(isolate.execute( - "text_encoding_test.js", - include_str!("text_encoding_test.js"), - )); + isolate + .execute( + "text_encoding_test.js", + include_str!("text_encoding_test.js"), + ) + .unwrap(); if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { unreachable!(); }