diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/bindings.rs | 208 | ||||
-rw-r--r-- | core/es_isolate.rs | 8 | ||||
-rw-r--r-- | core/isolate.rs | 63 | ||||
-rw-r--r-- | core/js_errors.rs | 423 | ||||
-rw-r--r-- | core/lib.rs | 8 |
5 files changed, 152 insertions, 558 deletions
diff --git a/core/bindings.rs b/core/bindings.rs index 9a0768f23..cb9c15ee1 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::Message>, -) -> 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, "<unknown>").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 bc412c7b2..a43358a24 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 a9e5f44a5..cb4bdaaf4 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<v8::Function>, pub(crate) pending_promise_exceptions: HashMap<i32, v8::Global<v8::Value>>, shared_isolate_handle: Arc<Mutex<Option<*mut v8::Isolate>>>, - pub(crate) js_error_create_fn: Arc<JSErrorCreateFn>, + pub(crate) js_error_create_fn: Box<JSErrorCreateFn>, needs_init: bool, pub(crate) shared: SharedQueue, pending_ops: FuturesUnordered<PendingOpFuture>, @@ -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<F>(&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<v8::Value>, js_error_create_fn: &JSErrorCreateFn, ) -> Result<T, ErrBox> { - // 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<v8::Message>, -) -> 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<T>(r: Result<T, ErrBox>) -> T { if let Err(e) = r { panic!(e.to_string()); diff --git a/core/js_errors.rs b/core/js_errors.rs index 6cb86f724..4d6110c5f 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 StackFrame { - pub line: 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 struct JSError { pub message: String, - pub source_line: Option<String>, pub script_resource_name: Option<String>, pub line_number: Option<i64>, - pub start_position: Option<i64>, - pub end_position: Option<i64>, - pub error_level: Option<i64>, pub start_column: Option<i64>, pub end_column: Option<i64>, - - pub frames: Vec<StackFrame>, + pub frames: Vec<JSStackFrame>, } #[derive(Debug, PartialEq, Clone)] -pub struct CoreJSError(V8Exception); - -impl StackFrame { - // TODO Maybe use serde_derive? - fn from_json_value(v: &serde_json::Value) -> Option<Self> { - if !v.is_object() { - return None; - } - 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, - }) - } +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, } -impl V8Exception { - /// Creates a new V8Exception by parsing the raw exception JSON string from V8. - pub fn from_json(json_str: &str) -> Option<Self> { - let v = serde_json::from_str::<serde_json::Value>(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 JSError { + pub(crate) fn create(js_error: Self) -> ErrBox { + ErrBox::from(js_error) } - pub fn from_json_value(v: serde_json::Value) -> Option<Self> { - 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::<StackFrame>::new(); - for frame_v in frame_values { - match StackFrame::from_json_value(frame_v) { - None => return None, - Some(frame) => frames.push(frame), - } + pub fn from_v8_exception( + scope: &mut impl v8::InIsolate, + exception: v8::Local<v8::Value>, + ) -> 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(); + + let msg = v8::Exception::create_message(scope, exception); + + Self { + message: msg.get(scope).to_rust_string_lossy(scope), + script_resource_name: msg + .get_script_resource_name(scope) + .and_then(|v| v8::Local::<v8::String>::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(|| "<unknown>".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::<Vec<_>>() + }) + .unwrap_or_else(Vec::<_>::new), } - - 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) - } -} +impl Error for JSError {} -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::<serde_json::Value>( - 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::<serde_json::Value>( - 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 9387d0cab..c4fbb33f4 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; |