diff options
Diffstat (limited to 'core/error.rs')
-rw-r--r-- | core/error.rs | 160 |
1 files changed, 82 insertions, 78 deletions
diff --git a/core/error.rs b/core/error.rs index 106845f04..1fc4a1af7 100644 --- a/core/error.rs +++ b/core/error.rs @@ -90,7 +90,8 @@ pub fn get_custom_error_class(error: &Error) -> Option<&'static 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)] +#[derive(Debug, PartialEq, Clone, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] pub struct JsError { pub message: String, pub cause: Option<Box<JsError>>, @@ -103,7 +104,7 @@ pub struct JsError { pub stack: Option<String>, } -#[derive(Debug, PartialEq, Clone, serde::Deserialize)] +#[derive(Debug, PartialEq, Clone, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct JsStackFrame { pub type_name: Option<String>, @@ -190,84 +191,84 @@ impl JsError { let msg = v8::Exception::create_message(scope, exception); - let (message, frames, stack, cause) = - if is_instance_of_error(scope, exception) { - // The exception is a JS Error object. - let exception: v8::Local<v8::Object> = exception.try_into().unwrap(); - let cause = get_property(scope, exception, "cause"); - let e: NativeJsError = - serde_v8::from_v8(scope, exception.into()).unwrap(); - // Get the message by formatting error.name and error.message. - let name = e.name.unwrap_or_else(|| "Error".to_string()); - let message_prop = e.message.unwrap_or_else(|| "".to_string()); - let message = if !name.is_empty() && !message_prop.is_empty() { - format!("Uncaught {}: {}", name, message_prop) - } else if !name.is_empty() { - format!("Uncaught {}", name) - } else if !message_prop.is_empty() { - format!("Uncaught {}", message_prop) - } else { - "Uncaught".to_string() - }; - let cause = cause.and_then(|cause| { - if cause.is_undefined() || seen.contains(&cause) { - None - } else { - seen.insert(cause); - Some(Box::new(JsError::inner_from_v8_exception( - scope, cause, seen, - ))) - } - }); - - // Access error.stack to ensure that prepareStackTrace() has been called. - // This should populate error.__callSiteEvals. - let stack = get_property(scope, exception, "stack"); - let stack: Option<v8::Local<v8::String>> = - stack.and_then(|s| s.try_into().ok()); - let stack = stack.map(|s| s.to_rust_string_lossy(scope)); - - // Read an array of structured frames from error.__callSiteEvals. - let frames_v8 = get_property(scope, exception, "__callSiteEvals"); - // Ignore non-array values - let frames_v8: Option<v8::Local<v8::Array>> = - frames_v8.and_then(|a| a.try_into().ok()); - - // Convert them into Vec<JsStackFrame> - let frames: Vec<JsStackFrame> = match frames_v8 { - Some(frames_v8) => { - serde_v8::from_v8(scope, frames_v8.into()).unwrap() - } - None => vec![], - }; - (message, frames, stack, cause) + if is_instance_of_error(scope, exception) { + // The exception is a JS Error object. + let exception: v8::Local<v8::Object> = exception.try_into().unwrap(); + let cause = get_property(scope, exception, "cause"); + let e: NativeJsError = + serde_v8::from_v8(scope, exception.into()).unwrap(); + // Get the message by formatting error.name and error.message. + let name = e.name.unwrap_or_else(|| "Error".to_string()); + let message_prop = e.message.unwrap_or_else(|| "".to_string()); + let message = if !name.is_empty() && !message_prop.is_empty() { + format!("Uncaught {}: {}", name, message_prop) + } else if !name.is_empty() { + format!("Uncaught {}", name) + } else if !message_prop.is_empty() { + format!("Uncaught {}", message_prop) } 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![], - None, - None, - ) + "Uncaught".to_string() }; - - Self { - message, - cause, - 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) - .map(|v| v.to_rust_string_lossy(scope)), - line_number: msg.get_line_number(scope).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, - stack, + let cause = cause.and_then(|cause| { + if cause.is_undefined() || seen.contains(&cause) { + None + } else { + seen.insert(cause); + Some(Box::new(JsError::inner_from_v8_exception( + scope, cause, seen, + ))) + } + }); + + // Access error.stack to ensure that prepareStackTrace() has been called. + // This should populate error.__callSiteEvals. + let stack = get_property(scope, exception, "stack"); + let stack: Option<v8::Local<v8::String>> = + stack.and_then(|s| s.try_into().ok()); + let stack = stack.map(|s| s.to_rust_string_lossy(scope)); + + // Read an array of structured frames from error.__callSiteEvals. + let frames_v8 = get_property(scope, exception, "__callSiteEvals"); + // Ignore non-array values + let frames_v8: Option<v8::Local<v8::Array>> = + frames_v8.and_then(|a| a.try_into().ok()); + + // Convert them into Vec<JsStackFrame> + let frames: Vec<JsStackFrame> = match frames_v8 { + Some(frames_v8) => serde_v8::from_v8(scope, frames_v8.into()).unwrap(), + None => vec![], + }; + Self { + message, + cause, + 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) + .map(|v| v.to_rust_string_lossy(scope)), + line_number: msg.get_line_number(scope).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, + stack, + } + } else { + // The exception is not a JS Error object. + // Get the message given by V8::Exception::create_message(), and provide + // empty frames. + Self { + message: msg.get(scope).to_rust_string_lossy(scope), + cause: None, + script_resource_name: None, + source_line: None, + line_number: None, + start_column: None, + end_column: None, + frames: vec![], + stack: None, + } } } } @@ -337,6 +338,9 @@ pub(crate) fn is_instance_of_error<'s>( let mut maybe_prototype = value.to_object(scope).unwrap().get_prototype(scope); while let Some(prototype) = maybe_prototype { + if !prototype.is_object() { + return false; + } if prototype.strict_equals(error_prototype) { return true; } |