From eafd40feabaf14f9f88748c66fa319519fd69072 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 2 Mar 2020 14:20:16 -0800 Subject: [PATCH] Do not convert exceptions to JSON and back (#4214) --- cli/fmt_errors.rs | 100 ++++------ cli/js/format_error.ts | 6 - cli/js/globals.ts | 2 +- cli/js/repl.ts | 9 +- cli/op_error.rs | 15 +- cli/ops/errors.rs | 21 -- cli/ops/worker_host.rs | 11 +- cli/source_maps.rs | 158 +++++++-------- cli/worker.rs | 4 +- core/bindings.rs | 208 ++------------------ core/es_isolate.rs | 8 +- core/isolate.rs | 63 ++---- core/js_errors.rs | 431 +++++++++++------------------------------ core/lib.rs | 8 +- 14 files changed, 280 insertions(+), 764 deletions(-) diff --git a/cli/fmt_errors.rs b/cli/fmt_errors.rs index 2bf0e859b1..73d6bb2d23 100644 --- a/cli/fmt_errors.rs +++ b/cli/fmt_errors.rs @@ -3,11 +3,12 @@ use crate::colors; use crate::source_maps::apply_source_map; use crate::source_maps::SourceMapGetter; +use deno_core; use deno_core::ErrBox; -use deno_core::StackFrame; -use deno_core::V8Exception; +use deno_core::JSStackFrame; use std::error::Error; use std::fmt; +use std::ops::Deref; /// A trait which specifies parts of a diagnostic like item needs to be able to /// generate to conform its display to other diagnostic like items @@ -21,37 +22,37 @@ pub trait DisplayFormatter { fn format_source_name( script_name: String, - line: i64, + line_number: i64, column: i64, is_internal: bool, ) -> String { - let line = line + 1; + let line_number = line_number + 1; let column = column + 1; if is_internal { - format!("{}:{}:{}", script_name, line, column) + format!("{}:{}:{}", script_name, line_number, column) } else { let script_name_c = colors::cyan(script_name); - let line_c = colors::yellow(line.to_string()); + let line_c = colors::yellow(line_number.to_string()); let column_c = colors::yellow(column.to_string()); format!("{}:{}:{}", script_name_c, line_c, column_c) } } -/// Formats optional source, line and column into a single string. +/// Formats optional source, line number and column into a single string. pub fn format_maybe_source_name( script_name: Option, - line: Option, + line_number: Option, column: Option, ) -> String { if script_name.is_none() { return "".to_string(); } - assert!(line.is_some()); + assert!(line_number.is_some()); assert!(column.is_some()); format_source_name( script_name.unwrap(), - line.unwrap(), + line_number.unwrap(), column.unwrap(), false, ) @@ -80,11 +81,11 @@ pub fn format_maybe_source_line( assert!(start_column.is_some()); assert!(end_column.is_some()); - let line = (1 + line_number.unwrap()).to_string(); - let line_color = colors::black_on_white(line.to_string()); - let line_len = line.len(); + let line_number = (1 + line_number.unwrap()).to_string(); + let line_color = colors::black_on_white(line_number.to_string()); + let line_number_len = line_number.len(); let line_padding = - colors::black_on_white(format!("{:indent$}", "", indent = line_len)) + colors::black_on_white(format!("{:indent$}", "", indent = line_number_len)) .to_string(); let mut s = String::new(); let start_column = start_column.unwrap(); @@ -124,7 +125,7 @@ pub fn format_error_message(msg: String) -> String { format!("{} {}", preamble, msg) } -fn format_stack_frame(frame: &StackFrame, is_internal_frame: bool) -> String { +fn format_stack_frame(frame: &JSStackFrame, is_internal_frame: bool) -> String { // Note when we print to string, we change from 0-indexed to 1-indexed. let function_name = if is_internal_frame { colors::italic_bold_gray(frame.function_name.clone()).to_string() @@ -133,7 +134,7 @@ fn format_stack_frame(frame: &StackFrame, is_internal_frame: bool) -> String { }; let mut source_loc = format_source_name( frame.script_name.clone(), - frame.line, + frame.line_number, frame.column, is_internal_frame, ); @@ -160,37 +161,25 @@ fn format_stack_frame(frame: &StackFrame, is_internal_frame: bool) -> String { } } -/// Wrapper around V8Exception which provides color to_string. +/// Wrapper around deno_core::JSError which provides color to_string. #[derive(Debug)] -pub struct JSError(V8Exception); +pub struct JSError(deno_core::JSError); impl JSError { - pub fn new(v8_exception: V8Exception) -> Self { - Self(v8_exception) - } - - pub fn from_json( - json_str: &str, + pub fn create( + core_js_error: deno_core::JSError, source_map_getter: &impl SourceMapGetter, ) -> ErrBox { - let unmapped_exception = V8Exception::from_json(json_str).unwrap(); - Self::from_v8_exception(unmapped_exception, source_map_getter) - } - - pub fn from_v8_exception( - unmapped_exception: V8Exception, - source_map_getter: &impl SourceMapGetter, - ) -> ErrBox { - let mapped_exception = - apply_source_map(&unmapped_exception, source_map_getter); - let js_error = Self(mapped_exception); + let core_js_error = apply_source_map(&core_js_error, source_map_getter); + let js_error = Self(core_js_error); ErrBox::from(js_error) } } -impl Into for JSError { - fn into(self) -> V8Exception { - self.0 +impl Deref for JSError { + type Target = deno_core::JSError; + fn deref(&self) -> &Self::Target { + &self.0 } } @@ -264,53 +253,46 @@ mod tests { use super::*; use crate::colors::strip_ansi_codes; - fn error1() -> V8Exception { - V8Exception { + #[test] + fn js_error_to_string() { + let core_js_error = deno_core::JSError { message: "Error: foo bar".to_string(), source_line: None, script_resource_name: None, line_number: None, - start_position: None, - end_position: None, - error_level: None, start_column: None, end_column: None, frames: vec![ - StackFrame { - line: 4, + JSStackFrame { + line_number: 4, column: 16, script_name: "foo_bar.ts".to_string(), function_name: "foo".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, - StackFrame { - line: 5, + JSStackFrame { + line_number: 5, column: 20, script_name: "bar_baz.ts".to_string(), function_name: "qat".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, - StackFrame { - line: 1, + JSStackFrame { + line_number: 1, column: 1, script_name: "deno_main.js".to_string(), function_name: "".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, ], - } - } - - #[test] - fn js_error_to_string() { - let e = error1(); - assert_eq!("error: Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2", strip_ansi_codes(&JSError(e).to_string())); + }; + let formatted_error = JSError(core_js_error).to_string(); + let actual = strip_ansi_codes(&formatted_error); + let expected = "error: Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2"; + assert_eq!(actual, expected); } #[test] diff --git a/cli/js/format_error.ts b/cli/js/format_error.ts index 21c4bcfa86..0a322338ab 100644 --- a/cli/js/format_error.ts +++ b/cli/js/format_error.ts @@ -2,12 +2,6 @@ import { DiagnosticItem } from "./diagnostics.ts"; import { sendSync } from "./dispatch_json.ts"; -// TODO(bartlomieju): move to `repl.ts`? -export function formatError(errString: string): string { - const res = sendSync("op_format_error", { error: errString }); - return res.error; -} - /** * Format an array of diagnostic items and return them as a single string. * @param items An array of diagnostic items to format diff --git a/cli/js/globals.ts b/cli/js/globals.ts index 9a7161ff05..fd2082e405 100644 --- a/cli/js/globals.ts +++ b/cli/js/globals.ts @@ -96,7 +96,7 @@ declare global { // eslint-disable-next-line @typescript-eslint/no-explicit-any evalContext(code: string): [any, EvalErrorInfo | null]; - errorToJSON: (e: Error) => string; + formatError: (e: Error) => string; } // Only `var` variables show up in the `globalThis` type when doing a global diff --git a/cli/js/repl.ts b/cli/js/repl.ts index 6e4f2545a2..0ef86ecd96 100644 --- a/cli/js/repl.ts +++ b/cli/js/repl.ts @@ -2,7 +2,6 @@ import { close } from "./files.ts"; import { exit } from "./os.ts"; import { core } from "./core.ts"; -import { formatError } from "./format_error.ts"; import { stringifyArgs } from "./console.ts"; import { sendSync, sendAsync } from "./dispatch_json.ts"; @@ -89,9 +88,7 @@ function evaluate(code: string): boolean { } else { lastThrownError = errInfo.thrown; if (errInfo.isNativeError) { - const formattedError = formatError( - core.errorToJSON(errInfo.thrown as Error) - ); + const formattedError = core.formatError(errInfo.thrown as Error); replError(formattedError); } else { replError("Thrown:", errInfo.thrown); @@ -162,7 +159,7 @@ export async function replLoop(): Promise { if (err.message !== "Interrupted") { // e.g. this happens when we have deno.close(3). // We want to display the problem. - const formattedError = formatError(core.errorToJSON(err)); + const formattedError = core.formatError(err); replError(formattedError); } // Quit REPL anyways. @@ -184,7 +181,7 @@ export async function replLoop(): Promise { } else { // e.g. this happens when we have deno.close(3). // We want to display the problem. - const formattedError = formatError(core.errorToJSON(err)); + const formattedError = core.formatError(err); replError(formattedError); quitRepl(1); } diff --git a/cli/op_error.rs b/cli/op_error.rs index 6a7dab20cf..d81d4cad16 100644 --- a/cli/op_error.rs +++ b/cli/op_error.rs @@ -3,19 +3,16 @@ //! There are many types of errors in Deno: //! - ErrBox: a generic boxed object. This is the super type of all //! errors handled in Rust. -//! - JSError: exceptions thrown from V8 into Rust. Usually a user exception. -//! These are basically a big JSON structure which holds information about -//! line numbers. We use this to pretty-print stack traces. These are -//! never passed back into the runtime. +//! - JSError: a container for the error message and stack trace for exceptions +//! thrown in JavaScript code. We use this to pretty-print stack traces. //! - OpError: these are errors that happen during ops, which are passed //! back into the runtime, where an exception object is created and thrown. -//! OpErrors have an integer code associated with them - access this via the `kind` field. +//! OpErrors have an integer code associated with them - access this via the +//! `kind` field. //! - Diagnostic: these are errors that originate in TypeScript's compiler. //! They're similar to JSError, in that they have line numbers. -//! But Diagnostics are compile-time type errors, whereas JSErrors are runtime exceptions. -//! -//! TODO: -//! - rename/merge JSError with V8Exception? +//! But Diagnostics are compile-time type errors, whereas JSErrors are runtime +//! exceptions. use crate::import_map::ImportMapError; use deno_core::ErrBox; diff --git a/cli/ops/errors.rs b/cli/ops/errors.rs index c588759cf3..4d2ce1befd 100644 --- a/cli/ops/errors.rs +++ b/cli/ops/errors.rs @@ -1,7 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; use crate::diagnostics::Diagnostic; -use crate::fmt_errors::JSError; use crate::op_error::OpError; use crate::source_maps::get_orig_position; use crate::source_maps::CachedMaps; @@ -14,32 +13,12 @@ pub fn init(i: &mut Isolate, s: &State) { "op_apply_source_map", s.stateful_json_op(op_apply_source_map), ); - i.register_op("op_format_error", s.stateful_json_op(op_format_error)); i.register_op( "op_format_diagnostic", s.stateful_json_op(op_format_diagnostic), ); } -#[derive(Deserialize)] -struct FormatErrorArgs { - error: String, -} - -fn op_format_error( - state: &State, - args: Value, - _zero_copy: Option, -) -> Result { - let args: FormatErrorArgs = serde_json::from_value(args)?; - let error = - JSError::from_json(&args.error, &state.borrow().global_state.ts_compiler); - - Ok(JsonOp::Sync(json!({ - "error": error.to_string(), - }))) -} - #[derive(Deserialize)] struct ApplySourceMap { filename: String, diff --git a/cli/ops/worker_host.rs b/cli/ops/worker_host.rs index 55afd6bf96..0690a3977c 100644 --- a/cli/ops/worker_host.rs +++ b/cli/ops/worker_host.rs @@ -215,15 +215,14 @@ fn serialize_worker_event(event: WorkerEvent) -> Value { } }); - if let Ok(err) = error.downcast::() { - let exception: V8Exception = err.into(); + if let Ok(js_error) = error.downcast::() { serialized_error = json!({ "type": "error", "error": { - "message": exception.message, - "fileName": exception.script_resource_name, - "lineNumber": exception.line_number, - "columnNumber": exception.start_column, + "message": js_error.message, + "fileName": js_error.script_resource_name, + "lineNumber": js_error.line_number, + "columnNumber": js_error.start_column, } }); } diff --git a/cli/source_maps.rs b/cli/source_maps.rs index 6b177cc696..9e29e554c6 100644 --- a/cli/source_maps.rs +++ b/cli/source_maps.rs @@ -1,7 +1,7 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -//! This mod provides functions to remap a deno_core::V8Exception based on a source map -use deno_core::StackFrame; -use deno_core::V8Exception; +//! This mod provides functions to remap a deno_core::deno_core::JSError based on a source map +use deno_core; +use deno_core::JSStackFrame; use serde_json; use source_map_mappings::parse_mappings; use source_map_mappings::Bias; @@ -12,7 +12,11 @@ use std::str; pub trait SourceMapGetter { /// Returns the raw source map file. fn get_source_map(&self, script_name: &str) -> Option>; - fn get_source_line(&self, script_name: &str, line: usize) -> Option; + fn get_source_line( + &self, + script_name: &str, + line_number: usize, + ) -> Option; } /// Cached filename lookups. The key can be None if a previous lookup failed to @@ -82,36 +86,36 @@ fn builtin_source_map(script_name: &str) -> Option> { } } -/// Apply a source map to a V8Exception, returning a V8Exception where the filenames, -/// the lines and the columns point to their original source location, not their -/// transpiled location if applicable. +/// Apply a source map to a deno_core::JSError, returning a JSError where file +/// names and line/column numbers point to the location in the original source, +/// rather than the transpiled source code. pub fn apply_source_map( - v8_exception: &V8Exception, + js_error: &deno_core::JSError, getter: &G, -) -> V8Exception { +) -> deno_core::JSError { let mut mappings_map: CachedMaps = HashMap::new(); - let mut frames = Vec::::new(); - for frame in &v8_exception.frames { + let mut frames = Vec::::new(); + for frame in &js_error.frames { let f = frame_apply_source_map(&frame, &mut mappings_map, getter); frames.push(f); } let (script_resource_name, line_number, start_column) = get_maybe_orig_position( - v8_exception.script_resource_name.clone(), - v8_exception.line_number, - v8_exception.start_column, + js_error.script_resource_name.clone(), + js_error.line_number, + js_error.start_column, &mut mappings_map, getter, ); // It is better to just move end_column to be the same distance away from // start column because sometimes the code point is not available in the // source file map. - let end_column = match v8_exception.end_column { + let end_column = match js_error.end_column { Some(ec) => { if let Some(sc) = start_column { - Some(ec - (v8_exception.start_column.unwrap() - sc)) + Some(ec - (js_error.start_column.unwrap() - sc)) } else { None } @@ -122,74 +126,67 @@ pub fn apply_source_map( // will go fetch it from the getter let source_line = match line_number { Some(ln) - if v8_exception.source_line.is_some() - && script_resource_name.is_some() => + if js_error.source_line.is_some() && script_resource_name.is_some() => { getter.get_source_line( - &v8_exception.script_resource_name.clone().unwrap(), + &js_error.script_resource_name.clone().unwrap(), ln as usize, ) } - _ => v8_exception.source_line.clone(), + _ => js_error.source_line.clone(), }; - V8Exception { - message: v8_exception.message.clone(), - frames, - error_level: v8_exception.error_level, + deno_core::JSError { + message: js_error.message.clone(), source_line, script_resource_name, line_number, start_column, end_column, - // These are difficult to map to their original position and they are not - // currently used in any output, so we don't remap them. - start_position: v8_exception.start_position, - end_position: v8_exception.end_position, + frames, } } fn frame_apply_source_map( - frame: &StackFrame, + frame: &JSStackFrame, mappings_map: &mut CachedMaps, getter: &G, -) -> StackFrame { - let (script_name, line, column) = get_orig_position( +) -> JSStackFrame { + let (script_name, line_number, column) = get_orig_position( frame.script_name.to_string(), - frame.line, + frame.line_number, frame.column, mappings_map, getter, ); - StackFrame { + JSStackFrame { script_name, function_name: frame.function_name.clone(), - line, + line_number, column, is_eval: frame.is_eval, is_constructor: frame.is_constructor, - is_wasm: frame.is_wasm, } } fn get_maybe_orig_position( script_name: Option, - line: Option, + line_number: Option, column: Option, mappings_map: &mut CachedMaps, getter: &G, ) -> (Option, Option, Option) { - match (script_name, line, column) { + match (script_name, line_number, column) { (Some(script_name_v), Some(line_v), Some(column_v)) => { - let (script_name, line, column) = get_orig_position( + let (script_name, line_number, column) = get_orig_position( script_name_v, line_v - 1, column_v, mappings_map, getter, ); - (Some(script_name), Some(line), Some(column)) + (Some(script_name), Some(line_number), Some(column)) } _ => (None, None, None), } @@ -197,18 +194,18 @@ fn get_maybe_orig_position( pub fn get_orig_position( script_name: String, - line: i64, + line_number: i64, column: i64, mappings_map: &mut CachedMaps, getter: &G, ) -> (String, i64, i64) { let maybe_sm = get_mappings(&script_name, mappings_map, getter); - let default_pos = (script_name, line, column); + let default_pos = (script_name, line_number, column); match maybe_sm { None => default_pos, Some(sm) => match sm.mappings.original_location_for( - line as u32, + line_number as u32, column as u32, Bias::default(), ) { @@ -274,7 +271,7 @@ mod tests { fn get_source_line( &self, script_name: &str, - line: usize, + line_number: usize, ) -> Option { let s = match script_name { "foo_bar.ts" => vec![ @@ -286,99 +283,83 @@ mod tests { ], _ => return None, }; - if s.len() > line { - Some(s[line].to_string()) + if s.len() > line_number { + Some(s[line_number].to_string()) } else { None } } } - fn error1() -> V8Exception { - V8Exception { + #[test] + fn apply_source_map_1() { + let core_js_error = deno_core::JSError { message: "Error: foo bar".to_string(), source_line: None, script_resource_name: None, line_number: None, - start_position: None, - end_position: None, - error_level: None, start_column: None, end_column: None, frames: vec![ - StackFrame { - line: 4, + JSStackFrame { + line_number: 4, column: 16, script_name: "foo_bar.ts".to_string(), function_name: "foo".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, - StackFrame { - line: 5, + JSStackFrame { + line_number: 5, column: 20, script_name: "bar_baz.ts".to_string(), function_name: "qat".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, - StackFrame { - line: 1, + JSStackFrame { + line_number: 1, column: 1, script_name: "deno_main.js".to_string(), function_name: "".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, ], - } - } - - #[test] - fn v8_exception_apply_source_map_1() { - let e = error1(); + }; let getter = MockSourceMapGetter {}; - let actual = apply_source_map(&e, &getter); - let expected = V8Exception { + let actual = apply_source_map(&core_js_error, &getter); + let expected = deno_core::JSError { message: "Error: foo bar".to_string(), source_line: None, script_resource_name: None, line_number: None, - start_position: None, - end_position: None, - error_level: None, start_column: None, end_column: None, frames: vec![ - StackFrame { - line: 5, + JSStackFrame { + line_number: 5, column: 12, script_name: "foo_bar.ts".to_string(), function_name: "foo".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, - StackFrame { - line: 4, + JSStackFrame { + line_number: 4, column: 14, script_name: "bar_baz.ts".to_string(), function_name: "qat".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, - StackFrame { - line: 1, + JSStackFrame { + line_number: 1, column: 1, script_name: "deno_main.js".to_string(), function_name: "".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, ], }; @@ -386,25 +367,21 @@ mod tests { } #[test] - fn v8_exception_apply_source_map_2() { - let e = V8Exception { + fn apply_source_map_2() { + let e = deno_core::JSError { message: "TypeError: baz".to_string(), source_line: None, script_resource_name: None, line_number: None, - start_position: None, - end_position: None, - error_level: None, start_column: None, end_column: None, - frames: vec![StackFrame { - line: 11, + frames: vec![JSStackFrame { + line_number: 11, column: 12, script_name: "CLI_SNAPSHOT.js".to_string(), function_name: "setLogDebug".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }], }; let getter = MockSourceMapGetter {}; @@ -416,15 +393,12 @@ mod tests { } #[test] - fn v8_exception_apply_source_map_line() { - let e = V8Exception { + fn apply_source_map_line() { + let e = deno_core::JSError { message: "TypeError: baz".to_string(), source_line: Some("foo".to_string()), script_resource_name: Some("foo_bar.ts".to_string()), line_number: Some(4), - start_position: None, - end_position: None, - error_level: None, start_column: Some(16), end_column: None, frames: vec![], diff --git a/cli/worker.rs b/cli/worker.rs index 3bc424ebe7..370e3f2971 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -105,8 +105,8 @@ impl Worker { let mut isolate = deno_core::EsIsolate::new(loader, startup_data, false); let global_state_ = state.borrow().global_state.clone(); - isolate.set_js_error_create(move |v8_exception| { - JSError::from_v8_exception(v8_exception, &global_state_.ts_compiler) + isolate.set_js_error_create_fn(move |core_js_error| { + JSError::create(core_js_error, &global_state_.ts_compiler) }); let (internal_channels, external_channels) = create_channels(); diff --git a/core/bindings.rs b/core/bindings.rs index 9a0768f239..cb9c15ee1d 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -3,6 +3,7 @@ use crate::es_isolate::EsIsolate; use crate::isolate::Isolate; use crate::isolate::ZeroCopyBuf; +use crate::js_errors::JSError; use rusty_v8 as v8; use v8::MapFnTo; @@ -26,7 +27,7 @@ lazy_static! { function: eval_context.map_fn_to() }, v8::ExternalReference { - function: error_to_json.map_fn_to() + function: format_error.map_fn_to() }, v8::ExternalReference { getter: shared_getter.map_fn_to() @@ -146,13 +147,13 @@ pub fn initialize_context<'s>( eval_context_val.into(), ); - let mut error_to_json_tmpl = v8::FunctionTemplate::new(scope, error_to_json); - let error_to_json_val = - error_to_json_tmpl.get_function(scope, context).unwrap(); + let mut format_error_tmpl = v8::FunctionTemplate::new(scope, format_error); + let format_error_val = + format_error_tmpl.get_function(scope, context).unwrap(); core_val.set( context, - v8::String::new(scope, "errorToJSON").unwrap().into(), - error_to_json_val.into(), + v8::String::new(scope, "formatError").unwrap().into(), + format_error_val.into(), ); core_val.set_accessor( @@ -536,20 +537,18 @@ fn eval_context( rv.set(output.into()); } -fn error_to_json( +fn format_error( scope: v8::FunctionCallbackScope, args: v8::FunctionCallbackArguments, mut rv: v8::ReturnValue, ) { let deno_isolate: &mut Isolate = unsafe { &mut *(scope.isolate().get_data(0) as *mut Isolate) }; - let context = deno_isolate.global_context.get(scope).unwrap(); - - let message = v8::Exception::create_message(scope, args.get(0)); - let json_obj = encode_message_as_object(scope, message); - let json_string = v8::json::stringify(context, json_obj.into()).unwrap(); - - rv.set(json_string.into()); + let e = JSError::from_v8_exception(scope, args.get(0)); + let e = (deno_isolate.js_error_create_fn)(e); + let e = e.to_string(); + let e = v8::String::new(scope, &e).unwrap(); + rv.set(e.into()) } fn queue_microtask( @@ -638,184 +637,3 @@ pub fn module_resolve_callback<'s>( None } - -pub fn encode_message_as_object<'a>( - s: &mut impl v8::ToLocal<'a>, - message: v8::Local, -) -> v8::Local<'a, v8::Object> { - let context = s.get_current_context().unwrap(); - let json_obj = v8::Object::new(s); - - let exception_str = message.get(s); - json_obj.set( - context, - v8::String::new(s, "message").unwrap().into(), - exception_str.into(), - ); - - let script_resource_name = message - .get_script_resource_name(s) - .expect("Missing ScriptResourceName"); - json_obj.set( - context, - v8::String::new(s, "scriptResourceName").unwrap().into(), - script_resource_name, - ); - - let source_line = message - .get_source_line(s, context) - .expect("Missing SourceLine"); - json_obj.set( - context, - v8::String::new(s, "sourceLine").unwrap().into(), - source_line.into(), - ); - - let line_number = message - .get_line_number(context) - .expect("Missing LineNumber"); - json_obj.set( - context, - v8::String::new(s, "lineNumber").unwrap().into(), - v8::Integer::new(s, line_number as i32).into(), - ); - - json_obj.set( - context, - v8::String::new(s, "startPosition").unwrap().into(), - v8::Integer::new(s, message.get_start_position() as i32).into(), - ); - - json_obj.set( - context, - v8::String::new(s, "endPosition").unwrap().into(), - v8::Integer::new(s, message.get_end_position() as i32).into(), - ); - - json_obj.set( - context, - v8::String::new(s, "errorLevel").unwrap().into(), - v8::Integer::new(s, message.error_level() as i32).into(), - ); - - json_obj.set( - context, - v8::String::new(s, "startColumn").unwrap().into(), - v8::Integer::new(s, message.get_start_column() as i32).into(), - ); - - json_obj.set( - context, - v8::String::new(s, "endColumn").unwrap().into(), - v8::Integer::new(s, message.get_end_column() as i32).into(), - ); - - let is_shared_cross_origin = - v8::Boolean::new(s, message.is_shared_cross_origin()); - - json_obj.set( - context, - v8::String::new(s, "isSharedCrossOrigin").unwrap().into(), - is_shared_cross_origin.into(), - ); - - let is_opaque = v8::Boolean::new(s, message.is_opaque()); - - json_obj.set( - context, - v8::String::new(s, "isOpaque").unwrap().into(), - is_opaque.into(), - ); - - let frames = if let Some(stack_trace) = message.get_stack_trace(s) { - let count = stack_trace.get_frame_count() as i32; - let frames = v8::Array::new(s, count); - - for i in 0..count { - let frame = stack_trace - .get_frame(s, i as usize) - .expect("No frame found"); - let frame_obj = v8::Object::new(s); - frames.set(context, v8::Integer::new(s, i).into(), frame_obj.into()); - frame_obj.set( - context, - v8::String::new(s, "line").unwrap().into(), - v8::Integer::new(s, frame.get_line_number() as i32).into(), - ); - frame_obj.set( - context, - v8::String::new(s, "column").unwrap().into(), - v8::Integer::new(s, frame.get_column() as i32).into(), - ); - - if let Some(function_name) = frame.get_function_name(s) { - frame_obj.set( - context, - v8::String::new(s, "functionName").unwrap().into(), - function_name.into(), - ); - } - - let script_name = match frame.get_script_name_or_source_url(s) { - Some(name) => name, - None => v8::String::new(s, "").unwrap(), - }; - frame_obj.set( - context, - v8::String::new(s, "scriptName").unwrap().into(), - script_name.into(), - ); - - frame_obj.set( - context, - v8::String::new(s, "isEval").unwrap().into(), - v8::Boolean::new(s, frame.is_eval()).into(), - ); - - frame_obj.set( - context, - v8::String::new(s, "isConstructor").unwrap().into(), - v8::Boolean::new(s, frame.is_constructor()).into(), - ); - - frame_obj.set( - context, - v8::String::new(s, "isWasm").unwrap().into(), - v8::Boolean::new(s, frame.is_wasm()).into(), - ); - } - - frames - } else { - // No stack trace. We only have one stack frame of info.. - let frames = v8::Array::new(s, 1); - let frame_obj = v8::Object::new(s); - frames.set(context, v8::Integer::new(s, 0).into(), frame_obj.into()); - - frame_obj.set( - context, - v8::String::new(s, "scriptResourceName").unwrap().into(), - script_resource_name, - ); - frame_obj.set( - context, - v8::String::new(s, "line").unwrap().into(), - v8::Integer::new(s, line_number as i32).into(), - ); - frame_obj.set( - context, - v8::String::new(s, "column").unwrap().into(), - v8::Integer::new(s, message.get_start_column() as i32).into(), - ); - - frames - }; - - json_obj.set( - context, - v8::String::new(s, "frames").unwrap().into(), - frames.into(), - ); - - json_obj -} diff --git a/core/es_isolate.rs b/core/es_isolate.rs index bc412c7b2c..a43358a247 100644 --- a/core/es_isolate.rs +++ b/core/es_isolate.rs @@ -173,8 +173,8 @@ impl EsIsolate { /// Instantiates a ES module /// /// ErrBox can be downcast to a type that exposes additional information about - /// the V8 exception. By default this type is CoreJSError, however it may be a - /// different type if Isolate::set_js_error_create() has been used. + /// the V8 exception. By default this type is JSError, however it may be a + /// different type if Isolate::set_js_error_create_fn() has been used. fn mod_instantiate(&mut self, id: ModuleId) -> Result<(), ErrBox> { let v8_isolate = self.core_isolate.v8_isolate.as_mut().unwrap(); let js_error_create_fn = &*self.core_isolate.js_error_create_fn; @@ -218,8 +218,8 @@ impl EsIsolate { /// Evaluates an already instantiated ES module. /// /// ErrBox can be downcast to a type that exposes additional information about - /// the V8 exception. By default this type is CoreJSError, however it may be a - /// different type if Isolate::set_js_error_create() has been used. + /// the V8 exception. By default this type is JSError, however it may be a + /// different type if Isolate::set_js_error_create_fn() has been used. pub fn mod_evaluate(&mut self, id: ModuleId) -> Result<(), ErrBox> { let core_isolate = &mut self.core_isolate; let v8_isolate = core_isolate.v8_isolate.as_mut().unwrap(); diff --git a/core/isolate.rs b/core/isolate.rs index a9e5f44a55..cb4bdaaf47 100644 --- a/core/isolate.rs +++ b/core/isolate.rs @@ -9,8 +9,7 @@ use rusty_v8 as v8; use crate::any_error::ErrBox; use crate::bindings; -use crate::js_errors::CoreJSError; -use crate::js_errors::V8Exception; +use crate::js_errors::JSError; use crate::ops::*; use crate::shared_queue::SharedQueue; use crate::shared_queue::RECOMMENDED_SIZE; @@ -147,7 +146,7 @@ pub enum StartupData<'a> { None, } -type JSErrorCreateFn = dyn Fn(V8Exception) -> ErrBox; +type JSErrorCreateFn = dyn Fn(JSError) -> ErrBox; type IsolateErrorHandleFn = dyn FnMut(ErrBox) -> Result<(), ErrBox>; /// A single execution context of JavaScript. Corresponds roughly to the "Web @@ -169,7 +168,7 @@ pub struct Isolate { pub(crate) js_recv_cb: v8::Global, pub(crate) pending_promise_exceptions: HashMap>, shared_isolate_handle: Arc>>, - pub(crate) js_error_create_fn: Arc, + pub(crate) js_error_create_fn: Box, needs_init: bool, pub(crate) shared: SharedQueue, pending_ops: FuturesUnordered, @@ -304,7 +303,7 @@ impl Isolate { snapshot: load_snapshot, has_snapshotted: false, shared_isolate_handle: Arc::new(Mutex::new(None)), - js_error_create_fn: Arc::new(CoreJSError::from_v8_exception), + js_error_create_fn: Box::new(JSError::create), shared, needs_init, pending_ops: FuturesUnordered::new(), @@ -349,13 +348,13 @@ impl Isolate { } /// Allows a callback to be set whenever a V8 exception is made. This allows - /// the caller to wrap the V8Exception into an error. By default this callback - /// is set to CoreJSError::from_v8_exception. - pub fn set_js_error_create(&mut self, f: F) - where - F: Fn(V8Exception) -> ErrBox + 'static, - { - self.js_error_create_fn = Arc::new(f); + /// the caller to wrap the JSError into an error. By default this callback + /// is set to JSError::create. + pub fn set_js_error_create_fn( + &mut self, + f: impl Fn(JSError) -> ErrBox + 'static, + ) { + self.js_error_create_fn = Box::new(f); } /// Executes a bit of built-in JavaScript to provide Deno.sharedQueue. @@ -418,8 +417,8 @@ impl Isolate { /// Executes traditional JavaScript code (traditional = not ES modules) /// /// ErrBox can be downcast to a type that exposes additional information about - /// the V8 exception. By default this type is CoreJSError, however it may be a - /// different type if Isolate::set_js_error_create() has been used. + /// the V8 exception. By default this type is JSError, however it may be a + /// different type if Isolate::set_js_error_create_fn() has been used. pub fn execute( &mut self, js_filename: &str, @@ -460,8 +459,8 @@ impl Isolate { /// set to true. /// /// ErrBox can be downcast to a type that exposes additional information about - /// the V8 exception. By default this type is CoreJSError, however it may be a - /// different type if Isolate::set_js_error_create() has been used. + /// the V8 exception. By default this type is JSError, however it may be a + /// different type if Isolate::set_js_error_create_fn() has been used. pub fn snapshot(&mut self) -> v8::OwnedStartupData { assert!(self.snapshot_creator.is_some()); @@ -612,16 +611,11 @@ pub(crate) fn attach_handle_to_error( ErrWithV8Handle::new(scope, err, handle).into() } -pub(crate) fn exception_to_err_result<'a, T>( - scope: &mut impl v8::ToLocal<'a>, +pub(crate) fn exception_to_err_result<'s, T>( + scope: &mut impl v8::ToLocal<'s>, exception: v8::Local, js_error_create_fn: &JSErrorCreateFn, ) -> Result { - // Use a HandleScope because the functions below create a lot of - // local handles (in particular, `encode_message_as_json()` does). - let mut hs = v8::HandleScope::new(scope); - let scope = hs.enter(); - // TODO(piscisaureus): in rusty_v8, `is_execution_terminating()` should // also be implemented on `struct Isolate`. let is_terminating_exception = scope @@ -642,18 +636,13 @@ pub(crate) fn exception_to_err_result<'a, T>( // Maybe make a new exception object. if exception.is_null_or_undefined() { - let exception_str = - v8::String::new(scope, "execution terminated").unwrap(); - exception = v8::Exception::error(scope, exception_str); + let message = v8::String::new(scope, "execution terminated").unwrap(); + exception = v8::Exception::error(scope, message); } } - let message = v8::Exception::create_message(scope, exception); - // TODO(piscisaureus): don't encode the message as json first and then - // immediately parse it after. - let exception_json_str = encode_message_as_json(scope, message); - let v8_exception = V8Exception::from_json(&exception_json_str).unwrap(); - let js_error = (js_error_create_fn)(v8_exception); + let js_error = JSError::from_v8_exception(scope, exception); + let js_error = (js_error_create_fn)(js_error); if is_terminating_exception { // Re-enable exception termination. @@ -679,16 +668,6 @@ fn check_promise_exceptions<'s>( } } -pub fn encode_message_as_json<'a>( - scope: &mut impl v8::ToLocal<'a>, - message: v8::Local, -) -> String { - let context = scope.get_current_context().unwrap(); - let json_obj = bindings::encode_message_as_object(scope, message); - let json_string = v8::json::stringify(context, json_obj.into()).unwrap(); - json_string.to_rust_string_lossy(scope) -} - pub fn js_check(r: Result) -> T { if let Err(e) = r { panic!(e.to_string()); diff --git a/core/js_errors.rs b/core/js_errors.rs index 6cb86f7240..4d6110c5f9 100644 --- a/core/js_errors.rs +++ b/core/js_errors.rs @@ -9,200 +9,119 @@ // console.log(err.stack); // It would require calling into Rust from Error.prototype.prepareStackTrace. -use crate::any_error::ErrBox; -use serde_json; -use serde_json::value::Value; +use crate::ErrBox; +use rusty_v8 as v8; +use std::convert::TryFrom; +use std::convert::TryInto; use std::error::Error; use std::fmt; -use std::str; + +/// A `JSError` represents an exception coming from V8, with stack frames and +/// line numbers. The deno_cli crate defines another `JSError` type, which wraps +/// the one defined here, that adds source map support and colorful formatting. +#[derive(Debug, PartialEq, Clone)] +pub struct JSError { + pub message: String, + pub source_line: Option, + pub script_resource_name: Option, + pub line_number: Option, + pub start_column: Option, + pub end_column: Option, + pub frames: Vec, +} #[derive(Debug, PartialEq, Clone)] -pub struct StackFrame { - pub line: i64, // zero indexed - pub column: i64, // zero indexed +pub struct JSStackFrame { + pub line_number: i64, // zero indexed + pub column: i64, // zero indexed pub script_name: String, pub function_name: String, pub is_eval: bool, pub is_constructor: bool, - pub is_wasm: bool, } -#[derive(Debug, PartialEq, Clone)] -pub struct V8Exception { - pub message: String, +impl JSError { + pub(crate) fn create(js_error: Self) -> ErrBox { + ErrBox::from(js_error) + } - pub source_line: Option, - pub script_resource_name: Option, - pub line_number: Option, - pub start_position: Option, - pub end_position: Option, - pub error_level: Option, - pub start_column: Option, - pub end_column: Option, + pub fn from_v8_exception( + scope: &mut impl v8::InIsolate, + exception: v8::Local, + ) -> Self { + // Create a new HandleScope because we're creating a lot of new local + // handles below. + let mut hs = v8::HandleScope::new(scope); + let scope = hs.enter(); + let context = scope.get_current_context().unwrap(); - pub frames: Vec, -} + let msg = v8::Exception::create_message(scope, exception); -#[derive(Debug, PartialEq, Clone)] -pub struct CoreJSError(V8Exception); - -impl StackFrame { - // TODO Maybe use serde_derive? - fn from_json_value(v: &serde_json::Value) -> Option { - if !v.is_object() { - return None; + Self { + message: msg.get(scope).to_rust_string_lossy(scope), + script_resource_name: msg + .get_script_resource_name(scope) + .and_then(|v| v8::Local::::try_from(v).ok()) + .map(|v| v.to_rust_string_lossy(scope)), + source_line: msg + .get_source_line(scope, context) + .map(|v| v.to_rust_string_lossy(scope)), + line_number: msg.get_line_number(context).and_then(|v| v.try_into().ok()), + start_column: msg.get_start_column().try_into().ok(), + end_column: msg.get_end_column().try_into().ok(), + frames: msg + .get_stack_trace(scope) + .map(|stack_trace| { + (0..stack_trace.get_frame_count()) + .map(|i| { + let frame = stack_trace.get_frame(scope, i).unwrap(); + JSStackFrame { + line_number: frame + .get_line_number() + .checked_sub(1) + .and_then(|v| v.try_into().ok()) + .unwrap(), + column: frame + .get_column() + .checked_sub(1) + .and_then(|v| v.try_into().ok()) + .unwrap(), + script_name: frame + .get_script_name_or_source_url(scope) + .map(|v| v.to_rust_string_lossy(scope)) + .unwrap_or_else(|| "".to_owned()), + function_name: frame + .get_function_name(scope) + .map(|v| v.to_rust_string_lossy(scope)) + .unwrap_or_else(|| "".to_owned()), + is_constructor: frame.is_constructor(), + is_eval: frame.is_eval(), + } + }) + .collect::>() + }) + .unwrap_or_else(Vec::<_>::new), } - let obj = v.as_object().unwrap(); - - let line_v = &obj["line"]; - if !line_v.is_u64() { - return None; - } - let line = line_v.as_u64().unwrap() as i64; - - let column_v = &obj["column"]; - if !column_v.is_u64() { - return None; - } - let column = column_v.as_u64().unwrap() as i64; - - let script_name_v = &obj["scriptName"]; - if !script_name_v.is_string() { - return None; - } - let script_name = String::from(script_name_v.as_str().unwrap()); - - // Optional fields. See EncodeExceptionAsJSON() in libdeno. - // Sometimes V8 doesn't provide all the frame information. - - let mut function_name = String::from(""); // default - if obj.contains_key("functionName") { - let function_name_v = &obj["functionName"]; - if function_name_v.is_string() { - function_name = String::from(function_name_v.as_str().unwrap()); - } - } - - let mut is_eval = false; // default - if obj.contains_key("isEval") { - let is_eval_v = &obj["isEval"]; - if is_eval_v.is_boolean() { - is_eval = is_eval_v.as_bool().unwrap(); - } - } - - let mut is_constructor = false; // default - if obj.contains_key("isConstructor") { - let is_constructor_v = &obj["isConstructor"]; - if is_constructor_v.is_boolean() { - is_constructor = is_constructor_v.as_bool().unwrap(); - } - } - - let mut is_wasm = false; // default - if obj.contains_key("isWasm") { - let is_wasm_v = &obj["isWasm"]; - if is_wasm_v.is_boolean() { - is_wasm = is_wasm_v.as_bool().unwrap(); - } - } - - Some(StackFrame { - line: line - 1, - column: column - 1, - script_name, - function_name, - is_eval, - is_constructor, - is_wasm, - }) } } -impl V8Exception { - /// Creates a new V8Exception by parsing the raw exception JSON string from V8. - pub fn from_json(json_str: &str) -> Option { - let v = serde_json::from_str::(json_str); - if let Err(err) = v { - eprintln!("V8Exception::from_json got problem {}", err); - return None; - } - let v = v.unwrap(); - Self::from_json_value(v) - } +impl Error for JSError {} - pub fn from_json_value(v: serde_json::Value) -> Option { - if !v.is_object() { - return None; - } - let obj = v.as_object().unwrap(); - - let message_v = &obj["message"]; - if !message_v.is_string() { - return None; - } - let message = String::from(message_v.as_str().unwrap()); - - let source_line = obj - .get("sourceLine") - .and_then(|v| v.as_str().map(String::from)); - let script_resource_name = obj - .get("scriptResourceName") - .and_then(|v| v.as_str().map(String::from)); - let line_number = obj.get("lineNumber").and_then(Value::as_i64); - let start_position = obj.get("startPosition").and_then(Value::as_i64); - let end_position = obj.get("endPosition").and_then(Value::as_i64); - let error_level = obj.get("errorLevel").and_then(Value::as_i64); - let start_column = obj.get("startColumn").and_then(Value::as_i64); - let end_column = obj.get("endColumn").and_then(Value::as_i64); - - let frames_v = &obj["frames"]; - if !frames_v.is_array() { - return None; - } - let frame_values = frames_v.as_array().unwrap(); - - let mut frames = Vec::::new(); - for frame_v in frame_values { - match StackFrame::from_json_value(frame_v) { - None => return None, - Some(frame) => frames.push(frame), - } - } - - Some(V8Exception { - message, - source_line, - script_resource_name, - line_number, - start_position, - end_position, - error_level, - start_column, - end_column, - frames, - }) - } -} - -impl CoreJSError { - pub fn from_v8_exception(v8_exception: V8Exception) -> ErrBox { - let error = Self(v8_exception); - ErrBox::from(error) - } -} - -fn format_source_loc(script_name: &str, line: i64, column: i64) -> String { +fn format_source_loc( + script_name: &str, + line_number: i64, + column: i64, +) -> String { // TODO match this style with how typescript displays errors. - let line = line + 1; + let line_number = line_number + 1; let column = column + 1; - format!("{}:{}:{}", script_name, line, column) + format!("{}:{}:{}", script_name, line_number, column) } -fn format_stack_frame(frame: &StackFrame) -> String { +fn format_stack_frame(frame: &JSStackFrame) -> String { // Note when we print to string, we change from 0-indexed to 1-indexed. let source_loc = - format_source_loc(&frame.script_name, frame.line, frame.column); + format_source_loc(&frame.script_name, frame.line_number, frame.column); if !frame.function_name.is_empty() { format!(" at {} ({})", frame.function_name, source_loc) @@ -213,25 +132,25 @@ fn format_stack_frame(frame: &StackFrame) -> String { } } -impl fmt::Display for CoreJSError { +impl fmt::Display for JSError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.0.script_resource_name.is_some() { - let script_resource_name = self.0.script_resource_name.as_ref().unwrap(); - if self.0.line_number.is_some() && self.0.start_column.is_some() { - assert!(self.0.line_number.is_some()); - assert!(self.0.start_column.is_some()); + if self.script_resource_name.is_some() { + let script_resource_name = self.script_resource_name.as_ref().unwrap(); + 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.0.line_number.unwrap() - 1, - self.0.start_column.unwrap() - 1, + self.line_number.unwrap() - 1, + self.start_column.unwrap() - 1, ); write!(f, "{}", source_loc)?; } - if self.0.source_line.is_some() { - write!(f, "\n{}\n", self.0.source_line.as_ref().unwrap())?; + if self.source_line.is_some() { + write!(f, "\n{}\n", self.source_line.as_ref().unwrap())?; let mut s = String::new(); - for i in 0..self.0.end_column.unwrap() { - if i >= self.0.start_column.unwrap() { + for i in 0..self.end_column.unwrap() { + if i >= self.start_column.unwrap() { s.push('^'); } else { s.push(' '); @@ -241,177 +160,57 @@ impl fmt::Display for CoreJSError { } } - write!(f, "{}", self.0.message)?; + write!(f, "{}", self.message)?; - for frame in &self.0.frames { + for frame in &self.frames { write!(f, "\n{}", format_stack_frame(frame))?; } Ok(()) } } -impl Error for CoreJSError {} - #[cfg(test)] mod tests { use super::*; - fn error1() -> V8Exception { - V8Exception { + #[test] + fn js_error_to_string() { + let js_error = JSError { message: "Error: foo bar".to_string(), source_line: None, script_resource_name: None, line_number: None, - start_position: None, - end_position: None, - error_level: None, start_column: None, end_column: None, frames: vec![ - StackFrame { - line: 4, + JSStackFrame { + line_number: 4, column: 16, script_name: "foo_bar.ts".to_string(), function_name: "foo".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, - StackFrame { - line: 5, + JSStackFrame { + line_number: 5, column: 20, script_name: "bar_baz.ts".to_string(), function_name: "qat".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, - StackFrame { - line: 1, + JSStackFrame { + line_number: 1, column: 1, script_name: "deno_main.js".to_string(), function_name: "".to_string(), is_eval: false, is_constructor: false, - is_wasm: false, }, ], - } - } - - #[test] - fn stack_frame_from_json_value_1() { - let v = serde_json::from_str::( - r#"{ - "line":2, - "column":11, - "functionName":"foo", - "scriptName":"/Users/rld/src/deno/cli/tests/error_001.ts", - "isEval":true, - "isConstructor":false, - "isWasm":false - }"#, - ) - .unwrap(); - let r = StackFrame::from_json_value(&v); - assert_eq!( - r, - Some(StackFrame { - line: 1, - column: 10, - script_name: "/Users/rld/src/deno/cli/tests/error_001.ts".to_string(), - function_name: "foo".to_string(), - is_eval: true, - is_constructor: false, - is_wasm: false, - }) - ); - } - - #[test] - fn stack_frame_from_json_value_2() { - let v = serde_json::from_str::( - r#"{ - "scriptName": "/Users/rld/src/deno/cli/tests/error_001.ts", - "line": 2, - "column": 11 - }"#, - ) - .unwrap(); - let r = StackFrame::from_json_value(&v); - assert!(r.is_some()); - let f = r.unwrap(); - assert_eq!(f.line, 1); - assert_eq!(f.column, 10); - assert_eq!(f.script_name, "/Users/rld/src/deno/cli/tests/error_001.ts"); - } - - #[test] - fn v8_exception_from_json() { - let r = V8Exception::from_json( - r#"{ - "message":"Uncaught Error: bad", - "frames":[ - { - "line":2, - "column":11, - "functionName":"foo", - "scriptName":"/Users/rld/src/deno/cli/tests/error_001.ts", - "isEval":true, - "isConstructor":false, - "isWasm":false - }, { - "line":5, - "column":5, - "functionName":"bar", - "scriptName":"/Users/rld/src/deno/cli/tests/error_001.ts", - "isEval":true, - "isConstructor":false, - "isWasm":false - } - ]}"#, - ); - assert!(r.is_some()); - let e = r.unwrap(); - assert_eq!(e.message, "Uncaught Error: bad"); - assert_eq!(e.frames.len(), 2); - assert_eq!( - e.frames[0], - StackFrame { - line: 1, - column: 10, - script_name: "/Users/rld/src/deno/cli/tests/error_001.ts".to_string(), - function_name: "foo".to_string(), - is_eval: true, - is_constructor: false, - is_wasm: false, - } - ) - } - - #[test] - fn v8_exception_from_json_2() { - let r = V8Exception::from_json( - "{\"message\":\"Error: boo\",\"sourceLine\":\"throw Error('boo');\",\"scriptResourceName\":\"a.js\",\"lineNumber\":3,\"startPosition\":8,\"endPosition\":9,\"errorLevel\":8,\"startColumn\":6,\"endColumn\":7,\"isSharedCrossOrigin\":false,\"isOpaque\":false,\"frames\":[{\"line\":3,\"column\":7,\"functionName\":\"\",\"scriptName\":\"a.js\",\"isEval\":false,\"isConstructor\":false,\"isWasm\":false}]}" - ); - assert!(r.is_some()); - let e = r.unwrap(); - assert_eq!(e.message, "Error: boo"); - assert_eq!(e.source_line, Some("throw Error('boo');".to_string())); - assert_eq!(e.script_resource_name, Some("a.js".to_string())); - assert_eq!(e.line_number, Some(3)); - assert_eq!(e.start_position, Some(8)); - assert_eq!(e.end_position, Some(9)); - assert_eq!(e.error_level, Some(8)); - assert_eq!(e.start_column, Some(6)); - assert_eq!(e.end_column, Some(7)); - assert_eq!(e.frames.len(), 1); - } - - #[test] - fn js_error_to_string() { - let e = CoreJSError(error1()); + }; + let actual = js_error.to_string(); let expected = "Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2"; - assert_eq!(expected, &e.to_string()); + assert_eq!(actual, expected); } } diff --git a/core/lib.rs b/core/lib.rs index 9387d0caba..c4fbb33f4e 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -1,14 +1,12 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -#[macro_use] -extern crate log; -extern crate futures; -extern crate libc; #[macro_use] extern crate downcast_rs; -extern crate rusty_v8; +extern crate futures; #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate log; mod any_error; mod bindings;