diff options
author | Nayeem Rahman <nayeemrmn99@gmail.com> | 2020-04-10 17:26:52 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-10 18:26:52 +0200 |
commit | 8b4508338b15dcc08503553dc9179a154c0165c7 (patch) | |
tree | 82d3d283edcff994c5b998d0d948985dc04f1b8c | |
parent | 195ad4c6264c3563044480685931999ffa9d3d5c (diff) |
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>
-rw-r--r-- | cli/fmt_errors.rs | 18 | ||||
-rw-r--r-- | cli/js/error_stack.ts | 60 | ||||
-rw-r--r-- | cli/source_maps.rs | 28 | ||||
-rw-r--r-- | cli/tests/044_bad_resource.ts.out | 2 | ||||
-rw-r--r-- | cli/tests/error_004_missing_module.ts.out | 8 | ||||
-rw-r--r-- | cli/tests/error_005_missing_dynamic_import.ts.out | 8 | ||||
-rw-r--r-- | cli/tests/error_006_import_ext_failure.ts.out | 6 | ||||
-rw-r--r-- | cli/tests/error_011_bad_module_specifier.ts.out | 13 | ||||
-rw-r--r-- | cli/tests/error_012_bad_dynamic_import_specifier.ts.out | 13 | ||||
-rw-r--r-- | cli/tests/error_type_definitions.ts.out | 14 | ||||
-rw-r--r-- | core/js_errors.rs | 135 |
11 files changed, 257 insertions, 48 deletions
diff --git a/cli/fmt_errors.rs b/cli/fmt_errors.rs index 5ba0bce77..b7cfbbce9 100644 --- a/cli/fmt_errors.rs +++ b/cli/fmt_errors.rs @@ -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() { - format!("{} {} {}", at_prefix, function_name, source_loc) + 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); diff --git a/cli/js/error_stack.ts b/cli/js/error_stack.ts index 97e9f68de..bab179e48 100644 --- a/cli/js/error_stack.ts +++ b/cli/js/error_stack.ts @@ -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 diff --git a/cli/source_maps.rs b/cli/source_maps.rs index bdfee11e9..b22cd4469 100644 --- a/cli/source_maps.rs +++ b/cli/source_maps.rs @@ -49,11 +49,16 @@ pub fn apply_source_map<G: SourceMapGetter>( ) -> deno_core::JSError { let mut mappings_map: CachedMaps = HashMap::new(); - 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); - } + 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); diff --git a/cli/tests/044_bad_resource.ts.out b/cli/tests/044_bad_resource.ts.out index 6009d2549..026beb341 100644 --- a/cli/tests/044_bad_resource.ts.out +++ b/cli/tests/044_bad_resource.ts.out @@ -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]) diff --git a/cli/tests/error_004_missing_module.ts.out b/cli/tests/error_004_missing_module.ts.out index 8e9f7f925..691d5ce5a 100644 --- a/cli/tests/error_004_missing_module.ts.out +++ b/cli/tests/error_004_missing_module.ts.out @@ -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]) diff --git a/cli/tests/error_005_missing_dynamic_import.ts.out b/cli/tests/error_005_missing_dynamic_import.ts.out index 7eb20fe04..4a7822e43 100644 --- a/cli/tests/error_005_missing_dynamic_import.ts.out +++ b/cli/tests/error_005_missing_dynamic_import.ts.out @@ -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]) diff --git a/cli/tests/error_006_import_ext_failure.ts.out b/cli/tests/error_006_import_ext_failure.ts.out index 41b9b7bf8..c4e6a1037 100644 --- a/cli/tests/error_006_import_ext_failure.ts.out +++ b/cli/tests/error_006_import_ext_failure.ts.out @@ -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]) diff --git a/cli/tests/error_011_bad_module_specifier.ts.out b/cli/tests/error_011_bad_module_specifier.ts.out index 773ddf8a8..5ac95002f 100644 --- a/cli/tests/error_011_bad_module_specifier.ts.out +++ b/cli/tests/error_011_bad_module_specifier.ts.out @@ -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]) diff --git a/cli/tests/error_012_bad_dynamic_import_specifier.ts.out b/cli/tests/error_012_bad_dynamic_import_specifier.ts.out index 618f45acd..505f29f82 100644 --- a/cli/tests/error_012_bad_dynamic_import_specifier.ts.out +++ b/cli/tests/error_012_bad_dynamic_import_specifier.ts.out @@ -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]) diff --git a/cli/tests/error_type_definitions.ts.out b/cli/tests/error_type_definitions.ts.out index 20c03d0be..59773ac35 100644 --- a/cli/tests/error_type_definitions.ts.out +++ b/cli/tests/error_type_definitions.ts.out @@ -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]) diff --git a/core/js_errors.rs b/core/js_errors.rs index 4d6110c5f..64009b9ee 100644 --- a/core/js_errors.rs +++ b/core/js_errors.rs @@ -18,7 +18,7 @@ use std::fmt; /// 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. +/// the one defined here, that adds source map support and colorful formatting. #[derive(Debug, PartialEq, Clone)] pub struct JSError { pub message: String, @@ -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"; |