mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
fix(core/js_error): Get frame data from prepareStackTrace() (#4690)
Fixes: #2703 Fixes: #2710 Closes: #4153 Closes: #4232 Co-authored-by: Kevin (Kun) Kassimo Qian <kevinkassimo@gmail.com>
This commit is contained in:
parent
195ad4c626
commit
8b4508338b
11 changed files with 257 additions and 48 deletions
|
@ -155,9 +155,21 @@ fn format_stack_frame(frame: &JSStackFrame, is_internal_frame: bool) -> String {
|
|||
source_loc = colors::gray(source_loc).to_string();
|
||||
}
|
||||
if !frame.function_name.is_empty() {
|
||||
if frame.is_async {
|
||||
format!(
|
||||
"{} {} {} {}",
|
||||
at_prefix,
|
||||
colors::gray("async".to_owned()).to_string(),
|
||||
function_name,
|
||||
source_loc
|
||||
)
|
||||
} else {
|
||||
format!("{} {} {}", at_prefix, function_name, source_loc)
|
||||
}
|
||||
} else if frame.is_eval {
|
||||
format!("{} eval {}", at_prefix, source_loc)
|
||||
} else if frame.is_async {
|
||||
format!("{} async {}", at_prefix, source_loc)
|
||||
} else {
|
||||
format!("{} {}", at_prefix, source_loc)
|
||||
}
|
||||
|
@ -272,6 +284,7 @@ mod tests {
|
|||
function_name: "foo".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
JSStackFrame {
|
||||
line_number: 5,
|
||||
|
@ -280,6 +293,7 @@ mod tests {
|
|||
function_name: "qat".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
JSStackFrame {
|
||||
line_number: 1,
|
||||
|
@ -288,8 +302,10 @@ mod tests {
|
|||
function_name: "".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
],
|
||||
already_source_mapped: true,
|
||||
};
|
||||
let formatted_error = JSError(core_js_error).to_string();
|
||||
let actual = strip_ansi_codes(&formatted_error);
|
||||
|
|
|
@ -140,7 +140,7 @@ function callSiteToString(callSite: CallSite): string {
|
|||
result += "async ";
|
||||
}
|
||||
if (isPromiseAll) {
|
||||
result += `Promise.all (index ${callSite.getPromiseIndex})`;
|
||||
result += `Promise.all (index ${callSite.getPromiseIndex()})`;
|
||||
return result;
|
||||
}
|
||||
if (isMethodCall) {
|
||||
|
@ -163,11 +163,52 @@ function callSiteToString(callSite: CallSite): string {
|
|||
return result;
|
||||
}
|
||||
|
||||
interface CallSiteEval {
|
||||
this: unknown;
|
||||
typeName: string;
|
||||
function: Function;
|
||||
functionName: string;
|
||||
methodName: string;
|
||||
fileName: string;
|
||||
lineNumber: number | null;
|
||||
columnNumber: number | null;
|
||||
evalOrigin: string | null;
|
||||
isToplevel: boolean;
|
||||
isEval: boolean;
|
||||
isNative: boolean;
|
||||
isConstructor: boolean;
|
||||
isAsync: boolean;
|
||||
isPromiseAll: boolean;
|
||||
promiseIndex: number | null;
|
||||
}
|
||||
|
||||
function evaluateCallSite(callSite: CallSite): CallSiteEval {
|
||||
return {
|
||||
this: callSite.getThis(),
|
||||
typeName: callSite.getTypeName(),
|
||||
function: callSite.getFunction(),
|
||||
functionName: callSite.getFunctionName(),
|
||||
methodName: callSite.getMethodName(),
|
||||
fileName: callSite.getFileName(),
|
||||
lineNumber: callSite.getLineNumber(),
|
||||
columnNumber: callSite.getColumnNumber(),
|
||||
evalOrigin: callSite.getEvalOrigin(),
|
||||
isToplevel: callSite.isToplevel(),
|
||||
isEval: callSite.isEval(),
|
||||
isNative: callSite.isNative(),
|
||||
isConstructor: callSite.isConstructor(),
|
||||
isAsync: callSite.isAsync(),
|
||||
isPromiseAll: callSite.isPromiseAll(),
|
||||
promiseIndex: callSite.getPromiseIndex(),
|
||||
};
|
||||
}
|
||||
|
||||
function prepareStackTrace(
|
||||
error: Error,
|
||||
structuredStackTrace: CallSite[]
|
||||
): string {
|
||||
return (
|
||||
Object.defineProperty(error, "__callSiteEvals", { value: [] });
|
||||
const errorString =
|
||||
`${error.name}: ${error.message}\n` +
|
||||
structuredStackTrace
|
||||
.map(
|
||||
|
@ -188,9 +229,18 @@ function prepareStackTrace(
|
|||
return callSite;
|
||||
}
|
||||
)
|
||||
.map((callSite): string => ` at ${callSiteToString(callSite)}`)
|
||||
.join("\n")
|
||||
);
|
||||
.map((callSite): string => {
|
||||
const callSiteEv = Object.freeze(evaluateCallSite(callSite));
|
||||
if (callSiteEv.lineNumber != null && callSiteEv.columnNumber != null) {
|
||||
// @ts-ignore
|
||||
error["__callSiteEvals"].push(callSiteEv);
|
||||
}
|
||||
return ` at ${callSiteToString(callSite)}`;
|
||||
})
|
||||
.join("\n");
|
||||
// @ts-ignore
|
||||
Object.freeze(error["__callSiteEvals"]);
|
||||
return errorString;
|
||||
}
|
||||
|
||||
// @internal
|
||||
|
|
|
@ -49,11 +49,16 @@ pub fn apply_source_map<G: SourceMapGetter>(
|
|||
) -> deno_core::JSError {
|
||||
let mut mappings_map: CachedMaps = HashMap::new();
|
||||
|
||||
let frames = if !js_error.already_source_mapped {
|
||||
let mut frames = Vec::<JSStackFrame>::new();
|
||||
for frame in &js_error.frames {
|
||||
let f = frame_apply_source_map(&frame, &mut mappings_map, getter);
|
||||
frames.push(f);
|
||||
}
|
||||
frames
|
||||
} else {
|
||||
js_error.frames.clone()
|
||||
};
|
||||
|
||||
let (script_resource_name, line_number, start_column) =
|
||||
get_maybe_orig_position(
|
||||
|
@ -98,6 +103,7 @@ pub fn apply_source_map<G: SourceMapGetter>(
|
|||
start_column,
|
||||
end_column,
|
||||
frames,
|
||||
already_source_mapped: js_error.already_source_mapped,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +127,7 @@ fn frame_apply_source_map<G: SourceMapGetter>(
|
|||
column,
|
||||
is_eval: frame.is_eval,
|
||||
is_constructor: frame.is_constructor,
|
||||
is_async: frame.is_async,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,6 +262,7 @@ mod tests {
|
|||
function_name: "foo".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
JSStackFrame {
|
||||
line_number: 5,
|
||||
|
@ -263,6 +271,7 @@ mod tests {
|
|||
function_name: "qat".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
JSStackFrame {
|
||||
line_number: 1,
|
||||
|
@ -271,8 +280,10 @@ mod tests {
|
|||
function_name: "".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
],
|
||||
already_source_mapped: false,
|
||||
};
|
||||
let getter = MockSourceMapGetter {};
|
||||
let actual = apply_source_map(&core_js_error, &getter);
|
||||
|
@ -291,6 +302,7 @@ mod tests {
|
|||
function_name: "foo".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
JSStackFrame {
|
||||
line_number: 4,
|
||||
|
@ -299,6 +311,7 @@ mod tests {
|
|||
function_name: "qat".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
JSStackFrame {
|
||||
line_number: 1,
|
||||
|
@ -307,8 +320,10 @@ mod tests {
|
|||
function_name: "".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
],
|
||||
already_source_mapped: false,
|
||||
};
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
@ -329,7 +344,9 @@ mod tests {
|
|||
function_name: "setLogDebug".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
}],
|
||||
already_source_mapped: false,
|
||||
};
|
||||
let getter = MockSourceMapGetter {};
|
||||
let actual = apply_source_map(&e, &getter);
|
||||
|
@ -348,6 +365,7 @@ mod tests {
|
|||
start_column: Some(16),
|
||||
end_column: None,
|
||||
frames: vec![],
|
||||
already_source_mapped: false,
|
||||
};
|
||||
let getter = MockSourceMapGetter {};
|
||||
let actual = apply_source_map(&e, &getter);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught BadResource: Bad resource ID
|
||||
[WILDCARD]dispatch_json.ts:[WILDCARD]
|
||||
at BadResource ([WILDCARD]errors.ts:[WILDCARD])
|
||||
at unwrapResponse ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at sendAsync ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at async main ([WILDCARD]tests/044_bad_resource.ts:[WILDCARD])
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
[WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/bad-module.ts" from "[WILDCARD]/error_004_missing_module.ts"
|
||||
[WILDCARD]dispatch_json.ts:[WILDCARD]
|
||||
at NotFound ([WILDCARD]errors.ts:[WILDCARD])
|
||||
at unwrapResponse ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at sendAsync[WILDCARD] ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at sendAsync ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at async processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async compile ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async tsCompilerOnMessage ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async workerMessageRecvCallback ([WILDCARD]runtime_worker.ts:[WILDCARD])
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
[WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/bad-module.ts" from "[WILDCARD]/error_005_missing_dynamic_import.ts"
|
||||
[WILDCARD]dispatch_json.ts:[WILDCARD]
|
||||
at NotFound ([WILDCARD]errors.ts:[WILDCARD])
|
||||
at unwrapResponse ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at sendAsync[WILDCARD] ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at sendAsync ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at async processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async compile ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async tsCompilerOnMessage ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async workerMessageRecvCallback ([WILDCARD]runtime_worker.ts:[WILDCARD])
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
[WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/non-existent" from "[WILDCARD]/error_006_import_ext_failure.ts"
|
||||
[WILDCARD]dispatch_json.ts:[WILDCARD]
|
||||
at NotFound ([WILDCARD]errors.ts:[WILDCARD])
|
||||
at unwrapResponse ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at sendAsync[WILDCARD] ([WILDCARD]dispatch_json.ts:[WILDCARD])
|
||||
at async processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async compile ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async tsCompilerOnMessage ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async workerMessageRecvCallback ([WILDCARD]runtime_worker.ts:[WILDCARD])
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
[WILDCARD]error: Uncaught URIError: relative import path "bad-module.ts" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_011_bad_module_specifier.ts"
|
||||
[WILDCARD]dispatch_json.ts:[WILDCARD]
|
||||
at unwrapResponse ($deno$/ops/dispatch_json.ts:[WILDCARD])
|
||||
at sendSync ($deno$/ops/dispatch_json.ts:[WILDCARD])
|
||||
at resolveModules ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at processImports ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at processImports ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at unwrapResponse ([WILDCARD]ops/dispatch_json.ts:[WILDCARD])
|
||||
at sendSync ([WILDCARD]ops/dispatch_json.ts:[WILDCARD])
|
||||
at resolveModules ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async compile ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async tsCompilerOnMessage ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async workerMessageRecvCallback ([WILDCARD]runtime_worker.ts:[WILDCARD])
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
[WILDCARD]error: Uncaught URIError: relative import path "bad-module.ts" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_012_bad_dynamic_import_specifier.ts"
|
||||
[WILDCARD]dispatch_json.ts:[WILDCARD]
|
||||
at unwrapResponse ($deno$/ops/dispatch_json.ts:[WILDCARD])
|
||||
at sendSync ($deno$/ops/dispatch_json.ts:[WILDCARD])
|
||||
at resolveModules ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at processImports ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at processImports ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at unwrapResponse ([WILDCARD]ops/dispatch_json.ts:[WILDCARD])
|
||||
at sendSync ([WILDCARD]ops/dispatch_json.ts:[WILDCARD])
|
||||
at resolveModules ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async compile ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async tsCompilerOnMessage ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async workerMessageRecvCallback ([WILDCARD]runtime_worker.ts:[WILDCARD])
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
[WILDCARD]error: Uncaught URIError: relative import path "baz" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/type_definitions/bar.d.ts"
|
||||
[WILDCARD]dispatch_json.ts:[WILDCARD]
|
||||
at unwrapResponse ($deno$/ops/dispatch_json.ts:[WILDCARD])
|
||||
at sendSync ($deno$/ops/dispatch_json.ts:[WILDCARD])
|
||||
at resolveModules ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at processImports ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at processImports ($deno$/compiler/imports.ts:[WILDCARD])
|
||||
at unwrapResponse ([WILDCARD]ops/dispatch_json.ts:[WILDCARD])
|
||||
at sendSync ([WILDCARD]ops/dispatch_json.ts:[WILDCARD])
|
||||
at resolveModules ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async processImports ([WILDCARD]compiler/imports.ts:[WILDCARD])
|
||||
at async compile ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async tsCompilerOnMessage ([WILDCARD]compiler.ts:[WILDCARD])
|
||||
at async workerMessageRecvCallback ([WILDCARD]runtime_worker.ts:[WILDCARD])
|
||||
|
|
|
@ -28,6 +28,12 @@ pub struct JSError {
|
|||
pub start_column: Option<i64>,
|
||||
pub end_column: Option<i64>,
|
||||
pub frames: Vec<JSStackFrame>,
|
||||
// TODO: Remove this field. It is required because JSError::from_v8_exception
|
||||
// will generally (but not always) return stack frames passed from
|
||||
// `prepareStackTrace()` which have already been source-mapped, and we need a
|
||||
// flag saying not to do it again. Note: applies to `frames` but not
|
||||
// `source_line`.
|
||||
pub already_source_mapped: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
|
@ -38,6 +44,18 @@ pub struct JSStackFrame {
|
|||
pub function_name: String,
|
||||
pub is_eval: bool,
|
||||
pub is_constructor: bool,
|
||||
pub is_async: bool,
|
||||
// TODO(nayeemrmn): Support more CallSite fields.
|
||||
}
|
||||
|
||||
fn get_property<'a>(
|
||||
scope: &mut impl v8::ToLocal<'a>,
|
||||
context: v8::Local<v8::Context>,
|
||||
object: v8::Local<v8::Object>,
|
||||
key: &str,
|
||||
) -> Option<v8::Local<'a, v8::Value>> {
|
||||
let key = v8::String::new(scope, key).unwrap();
|
||||
object.get(scope, context, key.into())
|
||||
}
|
||||
|
||||
impl JSError {
|
||||
|
@ -53,23 +71,85 @@ impl JSError {
|
|||
// handles below.
|
||||
let mut hs = v8::HandleScope::new(scope);
|
||||
let scope = hs.enter();
|
||||
let context = scope.get_current_context().unwrap();
|
||||
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
|
||||
let exception: Option<v8::Local<v8::Object>> =
|
||||
exception.clone().try_into().ok();
|
||||
let _ = exception.map(|e| get_property(scope, context, e, "stack"));
|
||||
|
||||
let maybe_call_sites = exception
|
||||
.and_then(|e| get_property(scope, context, e, "__callSiteEvals"));
|
||||
let maybe_call_sites: Option<v8::Local<v8::Array>> =
|
||||
maybe_call_sites.and_then(|a| a.try_into().ok());
|
||||
|
||||
let already_source_mapped;
|
||||
let frames = if let Some(call_sites) = maybe_call_sites {
|
||||
already_source_mapped = true;
|
||||
let mut output: Vec<JSStackFrame> = vec![];
|
||||
for i in 0..call_sites.length() {
|
||||
let call_site: v8::Local<v8::Object> = call_sites
|
||||
.get_index(scope, context, i)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let line_number: v8::Local<v8::Integer> =
|
||||
get_property(scope, context, call_site, "lineNumber")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let line_number = line_number.value() - 1;
|
||||
let column_number: v8::Local<v8::Integer> =
|
||||
get_property(scope, context, call_site, "columnNumber")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let column_number = column_number.value() - 1;
|
||||
let file_name: Result<v8::Local<v8::String>, _> =
|
||||
get_property(scope, context, call_site, "fileName")
|
||||
.unwrap()
|
||||
.try_into();
|
||||
let file_name = file_name
|
||||
.map_or_else(|_| String::new(), |s| s.to_rust_string_lossy(scope));
|
||||
let function_name: Result<v8::Local<v8::String>, _> =
|
||||
get_property(scope, context, call_site, "functionName")
|
||||
.unwrap()
|
||||
.try_into();
|
||||
let function_name = function_name
|
||||
.map_or_else(|_| String::new(), |s| s.to_rust_string_lossy(scope));
|
||||
let is_constructor: v8::Local<v8::Boolean> =
|
||||
get_property(scope, context, call_site, "isConstructor")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let is_constructor = is_constructor.is_true();
|
||||
let is_eval: v8::Local<v8::Boolean> =
|
||||
get_property(scope, context, call_site, "isEval")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let is_eval = is_eval.is_true();
|
||||
let is_async: v8::Local<v8::Boolean> =
|
||||
get_property(scope, context, call_site, "isAsync")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let is_async = is_async.is_true();
|
||||
output.push(JSStackFrame {
|
||||
line_number,
|
||||
column: column_number,
|
||||
script_name: file_name,
|
||||
function_name,
|
||||
is_constructor,
|
||||
is_eval,
|
||||
is_async,
|
||||
});
|
||||
}
|
||||
output
|
||||
} else {
|
||||
already_source_mapped = false;
|
||||
msg
|
||||
.get_stack_trace(scope)
|
||||
.map(|stack_trace| {
|
||||
(0..stack_trace.get_frame_count())
|
||||
|
@ -96,11 +176,28 @@ impl JSError {
|
|||
.unwrap_or_else(|| "".to_owned()),
|
||||
is_constructor: frame.is_constructor(),
|
||||
is_eval: frame.is_eval(),
|
||||
is_async: false,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_else(Vec::<_>::new),
|
||||
.unwrap_or_else(Vec::<_>::new)
|
||||
};
|
||||
|
||||
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,
|
||||
already_source_mapped,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +224,8 @@ fn format_stack_frame(frame: &JSStackFrame) -> String {
|
|||
format!(" at {} ({})", frame.function_name, source_loc)
|
||||
} else if frame.is_eval {
|
||||
format!(" at eval ({})", source_loc)
|
||||
} else if frame.is_async {
|
||||
format!(" at async ({})", source_loc)
|
||||
} else {
|
||||
format!(" at {}", source_loc)
|
||||
}
|
||||
|
@ -190,6 +289,7 @@ mod tests {
|
|||
function_name: "foo".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
JSStackFrame {
|
||||
line_number: 5,
|
||||
|
@ -198,6 +298,7 @@ mod tests {
|
|||
function_name: "qat".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
JSStackFrame {
|
||||
line_number: 1,
|
||||
|
@ -206,8 +307,10 @@ mod tests {
|
|||
function_name: "".to_string(),
|
||||
is_eval: false,
|
||||
is_constructor: false,
|
||||
is_async: false,
|
||||
},
|
||||
],
|
||||
already_source_mapped: true,
|
||||
};
|
||||
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";
|
||||
|
|
Loading…
Reference in a new issue