refactor(core/js_error): Align JSStackFrame with CallSite (#4715)

Renames and adds missing fields to JSStackFrame from CallSite. Fixes #4705.

Cleans up base changes for line and column numbers.
This commit is contained in:
Nayeem Rahman 2020-04-13 15:54:16 +01:00 committed by GitHub
parent 5105c68399
commit 0ea6eb83a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 271 additions and 192 deletions

View file

@ -232,9 +232,10 @@ impl DisplayFormatter for DiagnosticItem {
}
fn format_source_line(&self, level: usize) -> String {
// Formatter expects 1-based line numbers, but ours are 0-based.
format_maybe_source_line(
self.source_line.clone(),
self.line_number,
self.line_number.map(|n| n + 1),
self.start_column,
self.end_column,
match self.category {
@ -246,10 +247,11 @@ impl DisplayFormatter for DiagnosticItem {
}
fn format_source_name(&self) -> String {
// Formatter expects 1-based line and column numbers, but ours are 0-based.
format_maybe_source_name(
self.script_resource_name.clone(),
self.line_number,
self.start_column,
self.line_number.map(|n| n + 1),
self.start_column.map(|n| n + 1),
)
}
}

View file

@ -21,34 +21,34 @@ pub trait DisplayFormatter {
}
fn format_source_name(
script_name: String,
file_name: String,
line_number: i64,
column: i64,
column_number: i64,
) -> String {
let line_number = line_number + 1;
let column = column + 1;
let script_name_c = colors::cyan(script_name);
let line_number = line_number;
let column_number = column_number;
let file_name_c = colors::cyan(file_name);
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)
let column_c = colors::yellow(column_number.to_string());
format!("{}:{}:{}", file_name_c, line_c, column_c)
}
/// Formats optional source, line number and column into a single string.
/// Formats optional source, line and column numbers into a single string.
pub fn format_maybe_source_name(
script_name: Option<String>,
file_name: Option<String>,
line_number: Option<i64>,
column: Option<i64>,
column_number: Option<i64>,
) -> String {
if script_name.is_none() {
if file_name.is_none() {
return "".to_string();
}
assert!(line_number.is_some());
assert!(column.is_some());
assert!(column_number.is_some());
format_source_name(
script_name.unwrap(),
file_name.unwrap(),
line_number.unwrap(),
column.unwrap(),
column_number.unwrap(),
)
}
@ -76,7 +76,7 @@ pub fn format_maybe_source_line(
assert!(start_column.is_some());
assert!(end_column.is_some());
let line_number = (1 + line_number.unwrap()).to_string();
let line_number = 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 =
@ -93,12 +93,11 @@ pub fn format_maybe_source_line(
} else {
'~'
};
for i in 0..end_column {
if i >= start_column {
s.push(underline_char);
} else {
s.push(' ');
}
for _i in 0..start_column {
s.push(' ');
}
for _i in 0..(end_column - start_column) {
s.push(underline_char);
}
let color_underline = if is_error {
colors::red(s).to_string()
@ -181,7 +180,7 @@ impl DisplayFormatter for JSError {
format_maybe_source_name(
e.script_resource_name.clone(),
e.line_number,
e.start_column,
e.start_column.map(|n| n + 1),
)
)
}
@ -223,7 +222,7 @@ mod tests {
Some(1),
Some(2),
);
assert_eq!(strip_ansi_codes(&actual), "file://foo/bar.ts:2:3");
assert_eq!(strip_ansi_codes(&actual), "file://foo/bar.ts:1:2");
}
#[test]
@ -244,7 +243,7 @@ mod tests {
);
assert_eq!(
strip_ansi_codes(&actual),
"\n\n9 console.log(\'foo\');\n ~~~\n"
"\n\n8 console.log(\'foo\');\n ~~~\n"
);
}

View file

@ -11,31 +11,31 @@ function patchCallSite(callSite: CallSite, location: Location): CallSite {
getThis(): unknown {
return callSite.getThis();
},
getTypeName(): string {
getTypeName(): string | null {
return callSite.getTypeName();
},
getFunction(): Function {
getFunction(): Function | null {
return callSite.getFunction();
},
getFunctionName(): string {
getFunctionName(): string | null {
return callSite.getFunctionName();
},
getMethodName(): string {
getMethodName(): string | null {
return callSite.getMethodName();
},
getFileName(): string {
return location.filename;
getFileName(): string | null {
return location.fileName;
},
getLineNumber(): number {
return location.line;
return location.lineNumber;
},
getColumnNumber(): number {
return location.column;
return location.columnNumber;
},
getEvalOrigin(): string | null {
return callSite.getEvalOrigin();
},
isToplevel(): boolean {
isToplevel(): boolean | null {
return callSite.isToplevel();
},
isEval(): boolean {
@ -176,15 +176,15 @@ function callSiteToString(callSite: CallSite, isInternal = false): string {
interface CallSiteEval {
this: unknown;
typeName: string;
function: Function;
functionName: string;
methodName: string;
fileName: string;
typeName: string | null;
function: Function | null;
functionName: string | null;
methodName: string | null;
fileName: string | null;
lineNumber: number | null;
columnNumber: number | null;
evalOrigin: string | null;
isToplevel: boolean;
isToplevel: boolean | null;
isEval: boolean;
isNative: boolean;
isConstructor: boolean;
@ -227,16 +227,16 @@ function prepareStackTrace(
structuredStackTrace
.map(
(callSite): CallSite => {
const filename = callSite.getFileName();
const line = callSite.getLineNumber();
const column = callSite.getColumnNumber();
if (filename && line != null && column != null) {
const fileName = callSite.getFileName();
const lineNumber = callSite.getLineNumber();
const columnNumber = callSite.getColumnNumber();
if (fileName && lineNumber != null && columnNumber != null) {
return patchCallSite(
callSite,
applySourceMap({
filename,
line,
column,
fileName,
lineNumber,
columnNumber,
})
);
}
@ -244,16 +244,13 @@ function prepareStackTrace(
}
)
.map((callSite): string => {
// @ts-ignore
error.__callSiteEvals.push(Object.freeze(evaluateCallSite(callSite)));
const isInternal =
callSite.getFileName()?.startsWith("$deno$") ?? false;
const string = callSiteToString(callSite, isInternal);
const callSiteEv = Object.freeze(evaluateCallSite(callSite));
if (callSiteEv.lineNumber != null && callSiteEv.columnNumber != null) {
// @ts-ignore
error.__callSiteEvals.push(callSiteEv);
// @ts-ignore
error.__formattedFrames.push(string);
}
// @ts-ignore
error.__formattedFrames.push(string);
return ` at ${colors.stripColor(string)}`;
})
.join("\n");

View file

@ -31,15 +31,15 @@ import { core } from "./core.ts";
declare global {
interface CallSite {
getThis(): unknown;
getTypeName(): string;
getFunction(): Function;
getFunctionName(): string;
getMethodName(): string;
getFileName(): string;
getTypeName(): string | null;
getFunction(): Function | null;
getFunctionName(): string | null;
getMethodName(): string | null;
getFileName(): string | null;
getLineNumber(): number | null;
getColumnNumber(): number | null;
getEvalOrigin(): string | null;
isToplevel(): boolean;
isToplevel(): boolean | null;
isEval(): boolean;
isNative(): boolean;
isConstructor(): boolean;

View file

@ -1612,11 +1612,11 @@ declare namespace Deno {
interface Location {
/** The full url for the module, e.g. `file://some/file.ts` or
* `https://some/file.ts`. */
filename: string;
fileName: string;
/** The line number in the file. It is assumed to be 1-indexed. */
line: number;
lineNumber: number;
/** The column number in the file. It is assumed to be 1-indexed. */
column: number;
columnNumber: number;
}
/** UNSTABLE: new API, yet to be vetted.
@ -1636,9 +1636,9 @@ declare namespace Deno {
* An example:
*
* const orig = Deno.applySourceMap({
* location: "file://my/module.ts",
* line: 5,
* column: 15
* fileName: "file://my/module.ts",
* lineNumber: 5,
* columnNumber: 15
* });
* console.log(`${orig.filename}:${orig.line}:${orig.column}`);
*/

View file

@ -7,25 +7,21 @@ export function formatDiagnostics(items: DiagnosticItem[]): string {
}
export interface Location {
filename: string;
line: number;
column: number;
fileName: string;
lineNumber: number;
columnNumber: number;
}
export function applySourceMap(location: Location): Location {
const { filename, line, column } = location;
// On this side, line/column are 1 based, but in the source maps, they are
// 0 based, so we have to convert back and forth
const { fileName, lineNumber, columnNumber } = location;
const res = sendSync("op_apply_source_map", {
filename,
line: line - 1,
column: column - 1,
fileName,
lineNumber: lineNumber,
columnNumber: columnNumber,
});
return {
filename: res.filename,
line: res.line + 1,
column: res.column + 1,
fileName: res.fileName,
lineNumber: res.lineNumber,
columnNumber: res.columnNumber,
};
}

View file

@ -6,15 +6,15 @@ const { setPrepareStackTrace } = Deno[Deno.symbols.internal];
interface CallSite {
getThis(): unknown;
getTypeName(): string;
getFunction(): Function;
getFunctionName(): string;
getMethodName(): string;
getFileName(): string;
getTypeName(): string | null;
getFunction(): Function | null;
getFunctionName(): string | null;
getMethodName(): string | null;
getFileName(): string | null;
getLineNumber(): number | null;
getColumnNumber(): number | null;
getEvalOrigin(): string | null;
isToplevel(): boolean;
isToplevel(): boolean | null;
isEval(): boolean;
isNative(): boolean;
isConstructor(): boolean;
@ -24,9 +24,9 @@ interface CallSite {
}
function getMockCallSite(
filename: string,
line: number | null,
column: number | null
fileName: string,
lineNumber: number | null,
columnNumber: number | null
): CallSite {
return {
getThis(): unknown {
@ -45,13 +45,13 @@ function getMockCallSite(
return "";
},
getFileName(): string {
return filename;
return fileName;
},
getLineNumber(): number | null {
return line;
return lineNumber;
},
getColumnNumber(): number | null {
return column;
return columnNumber;
},
getEvalOrigin(): null {
return null;
@ -98,11 +98,11 @@ unitTest(function prepareStackTrace(): void {
unitTest(function applySourceMap(): void {
const result = Deno.applySourceMap({
filename: "CLI_SNAPSHOT.js",
line: 23,
column: 0,
fileName: "CLI_SNAPSHOT.js",
lineNumber: 23,
columnNumber: 0,
});
assert(result.filename.endsWith(".ts"));
assert(result.line != null);
assert(result.column != null);
assert(result.fileName.endsWith(".ts"));
assert(result.lineNumber != null);
assert(result.columnNumber != null);
});

View file

@ -20,10 +20,11 @@ pub fn init(i: &mut Isolate, s: &State) {
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct ApplySourceMap {
filename: String,
line: i32,
column: i32,
file_name: String,
line_number: i32,
column_number: i32,
}
fn op_apply_source_map(
@ -34,18 +35,19 @@ fn op_apply_source_map(
let args: ApplySourceMap = serde_json::from_value(args)?;
let mut mappings_map: CachedMaps = HashMap::new();
let (orig_filename, orig_line, orig_column) = get_orig_position(
args.filename,
args.line.into(),
args.column.into(),
&mut mappings_map,
&state.borrow().global_state.ts_compiler,
);
let (orig_file_name, orig_line_number, orig_column_number) =
get_orig_position(
args.file_name,
args.line_number.into(),
args.column_number.into(),
&mut mappings_map,
&state.borrow().global_state.ts_compiler,
);
Ok(JsonOp::Sync(json!({
"filename": orig_filename,
"line": orig_line as u32,
"column": orig_column as u32,
"fileName": orig_file_name,
"lineNumber": orig_line_number as u32,
"columnNumber": orig_column_number as u32,
})))
}

View file

@ -6,10 +6,10 @@ use std::str;
pub trait SourceMapGetter {
/// Returns the raw source map file.
fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>>;
fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>>;
fn get_source_line(
&self,
script_name: &str,
file_name: &str,
line_number: usize,
) -> Option<String>;
}
@ -29,10 +29,10 @@ fn builtin_source_map(_: &str) -> Option<Vec<u8>> {
}
#[cfg(not(feature = "check-only"))]
fn builtin_source_map(script_name: &str) -> Option<Vec<u8>> {
if script_name.ends_with("CLI_SNAPSHOT.js") {
fn builtin_source_map(file_name: &str) -> Option<Vec<u8>> {
if file_name.ends_with("CLI_SNAPSHOT.js") {
Some(crate::js::CLI_SNAPSHOT_MAP.to_vec())
} else if script_name.ends_with("COMPILER_SNAPSHOT.js") {
} else if file_name.ends_with("COMPILER_SNAPSHOT.js") {
Some(crate::js::COMPILER_SNAPSHOT_MAP.to_vec())
} else {
None
@ -54,10 +54,12 @@ pub fn apply_source_map<G: SourceMapGetter>(
get_maybe_orig_position(
js_error.script_resource_name.clone(),
js_error.line_number,
js_error.start_column,
// start_column is 0-based, we need 1-based here.
js_error.start_column.map(|n| n + 1),
&mut mappings_map,
getter,
);
let start_column = start_column.map(|n| n - 1);
// 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.
@ -79,7 +81,8 @@ pub fn apply_source_map<G: SourceMapGetter>(
{
getter.get_source_line(
&js_error.script_resource_name.clone().unwrap(),
ln as usize,
// Getter expects 0-based line numbers, but ours are 1-based.
ln as usize - 1,
)
}
_ => js_error.source_line.clone(),
@ -98,48 +101,47 @@ pub fn apply_source_map<G: SourceMapGetter>(
}
fn get_maybe_orig_position<G: SourceMapGetter>(
script_name: Option<String>,
file_name: Option<String>,
line_number: Option<i64>,
column: Option<i64>,
column_number: Option<i64>,
mappings_map: &mut CachedMaps,
getter: &G,
) -> (Option<String>, Option<i64>, Option<i64>) {
match (script_name, line_number, column) {
(Some(script_name_v), Some(line_v), Some(column_v)) => {
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_number), Some(column))
match (file_name, line_number, column_number) {
(Some(file_name_v), Some(line_v), Some(column_v)) => {
let (file_name, line_number, column_number) =
get_orig_position(file_name_v, line_v, column_v, mappings_map, getter);
(Some(file_name), Some(line_number), Some(column_number))
}
_ => (None, None, None),
}
}
pub fn get_orig_position<G: SourceMapGetter>(
script_name: String,
file_name: String,
line_number: i64,
column: i64,
column_number: i64,
mappings_map: &mut CachedMaps,
getter: &G,
) -> (String, i64, i64) {
let maybe_source_map = get_mappings(&script_name, mappings_map, getter);
let default_pos = (script_name, line_number, column);
let maybe_source_map = get_mappings(&file_name, mappings_map, getter);
let default_pos = (file_name, line_number, column_number);
// Lookup expects 0-based line and column numbers, but ours are 1-based.
let line_number = line_number - 1;
let column_number = column_number - 1;
match maybe_source_map {
None => default_pos,
Some(source_map) => {
match source_map.lookup_token(line_number as u32, column as u32) {
match source_map.lookup_token(line_number as u32, column_number as u32) {
None => default_pos,
Some(token) => match token.get_source() {
None => default_pos,
Some(original) => (
original.to_string(),
i64::from(token.get_src_line()),
i64::from(token.get_src_col()),
i64::from(token.get_src_line()) + 1,
i64::from(token.get_src_col()) + 1,
),
},
}
@ -148,23 +150,23 @@ pub fn get_orig_position<G: SourceMapGetter>(
}
fn get_mappings<'a, G: SourceMapGetter>(
script_name: &str,
file_name: &str,
mappings_map: &'a mut CachedMaps,
getter: &G,
) -> &'a Option<SourceMap> {
mappings_map
.entry(script_name.to_string())
.or_insert_with(|| parse_map_string(script_name, getter))
.entry(file_name.to_string())
.or_insert_with(|| parse_map_string(file_name, getter))
}
// TODO(kitsonk) parsed source maps should probably be cached in state in
// the module meta data.
fn parse_map_string<G: SourceMapGetter>(
script_name: &str,
file_name: &str,
getter: &G,
) -> Option<SourceMap> {
builtin_source_map(script_name)
.or_else(|| getter.get_source_map(script_name))
builtin_source_map(file_name)
.or_else(|| getter.get_source_map(file_name))
.and_then(|raw_source_map| SourceMap::from_slice(&raw_source_map).ok())
}
@ -175,8 +177,8 @@ mod tests {
struct MockSourceMapGetter {}
impl SourceMapGetter for MockSourceMapGetter {
fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>> {
let s = match script_name {
fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> {
let s = match file_name {
"foo_bar.ts" => {
r#"{"sources": ["foo_bar.ts"], "mappings":";;;IAIA,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}"#
}
@ -190,10 +192,10 @@ mod tests {
fn get_source_line(
&self,
script_name: &str,
file_name: &str,
line_number: usize,
) -> Option<String> {
let s = match script_name {
let s = match file_name {
"foo_bar.ts" => vec![
"console.log('foo');",
"console.log('foo');",

View file

@ -0,0 +1,13 @@
const p = Promise.all([
(async (): Promise<never> => {
await Promise.resolve();
throw new Error("Promise.all()");
})(),
]);
try {
await p;
} catch (error) {
console.log(error.stack);
throw error;
}

View file

@ -0,0 +1,8 @@
[WILDCARD]Error: Promise.all()
at [WILDCARD]tests/error_024_stack_promise_all.ts:[WILDCARD]
at async Promise.all (index 0)
at async [WILDCARD]tests/error_024_stack_promise_all.ts:[WILDCARD]
error: Uncaught Error: Promise.all()
at [WILDCARD]tests/error_024_stack_promise_all.ts:[WILDCARD]
at async Promise.all (index 0)
at async [WILDCARD]tests/error_024_stack_promise_all.ts:[WILDCARD]

View file

@ -1406,6 +1406,13 @@ itest!(error_023_stack_async {
exit_code: 1,
});
itest!(error_024_stack_promise_all {
args: "error_024_stack_promise_all.ts",
output: "error_024_stack_promise_all.ts.out",
check_stderr: true,
exit_code: 1,
});
itest!(error_syntax {
args: "run --reload error_syntax.js",
check_stderr: true,

View file

@ -1,8 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
// Note that source_map_mappings requires 0-indexed line and column numbers but
// V8 Exceptions are 1-indexed.
// TODO: This currently only applies to uncaught exceptions. It would be nice to
// also have source maps for situations like this:
// const err = new Error("Boo!");
@ -25,22 +22,28 @@ pub struct JSError {
pub source_line: Option<String>,
pub script_resource_name: Option<String>,
pub line_number: Option<i64>,
pub start_column: Option<i64>,
pub end_column: Option<i64>,
pub start_column: Option<i64>, // 0-based
pub end_column: Option<i64>, // 0-based
pub frames: Vec<JSStackFrame>,
pub formatted_frames: Vec<String>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct JSStackFrame {
pub line_number: i64, // zero indexed
pub column: i64, // zero indexed
pub script_name: String,
pub function_name: String,
pub type_name: Option<String>,
pub function_name: Option<String>,
pub method_name: Option<String>,
pub file_name: Option<String>,
pub line_number: Option<i64>,
pub column_number: Option<i64>,
pub eval_origin: Option<String>,
pub is_top_level: Option<bool>,
pub is_eval: bool,
pub is_native: bool,
pub is_constructor: bool,
pub is_async: bool,
// TODO(nayeemrmn): Support more CallSite fields.
pub is_promise_all: bool,
pub promise_index: Option<i64>,
}
fn get_property<'a>(
@ -96,56 +99,106 @@ impl JSError {
.unwrap()
.try_into()
.unwrap();
let line_number: v8::Local<v8::Integer> =
let type_name: Option<v8::Local<v8::String>> =
get_property(scope, context, call_site, "typeName")
.unwrap()
.try_into()
.ok();
let type_name = type_name.map(|s| s.to_rust_string_lossy(scope));
let function_name: Option<v8::Local<v8::String>> =
get_property(scope, context, call_site, "functionName")
.unwrap()
.try_into()
.ok();
let function_name =
function_name.map(|s| s.to_rust_string_lossy(scope));
let method_name: Option<v8::Local<v8::String>> =
get_property(scope, context, call_site, "methodName")
.unwrap()
.try_into()
.ok();
let method_name = method_name.map(|s| s.to_rust_string_lossy(scope));
let file_name: Option<v8::Local<v8::String>> =
get_property(scope, context, call_site, "fileName")
.unwrap()
.try_into()
.ok();
let file_name = file_name.map(|s| s.to_rust_string_lossy(scope));
let line_number: Option<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> =
.ok();
let line_number = line_number.map(|n| n.value());
let column_number: Option<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")
.ok();
let column_number = column_number.map(|n| n.value());
let eval_origin: Option<v8::Local<v8::String>> =
get_property(scope, context, call_site, "evalOrigin")
.unwrap()
.try_into()
.unwrap();
let is_constructor = is_constructor.is_true();
.ok();
let eval_origin = eval_origin.map(|s| s.to_rust_string_lossy(scope));
let is_top_level: Option<v8::Local<v8::Boolean>> =
get_property(scope, context, call_site, "isTopLevel")
.unwrap()
.try_into()
.ok();
let is_top_level = is_top_level.map(|b| b.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_native: v8::Local<v8::Boolean> =
get_property(scope, context, call_site, "isNative")
.unwrap()
.try_into()
.unwrap();
let is_native = is_native.is_true();
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_async: v8::Local<v8::Boolean> =
get_property(scope, context, call_site, "isAsync")
.unwrap()
.try_into()
.unwrap();
let is_async = is_async.is_true();
let is_promise_all: v8::Local<v8::Boolean> =
get_property(scope, context, call_site, "isPromiseAll")
.unwrap()
.try_into()
.unwrap();
let is_promise_all = is_promise_all.is_true();
let promise_index: Option<v8::Local<v8::Integer>> =
get_property(scope, context, call_site, "columnNumber")
.unwrap()
.try_into()
.ok();
let promise_index = promise_index.map(|n| n.value());
frames.push(JSStackFrame {
line_number,
column: column_number,
script_name: file_name,
type_name,
function_name,
is_constructor,
method_name,
file_name,
line_number,
column_number,
eval_origin,
is_top_level,
is_eval,
is_native,
is_constructor,
is_async,
is_promise_all,
promise_index,
});
let formatted_frame: v8::Local<v8::String> = formatted_frames_v8
.get_index(scope, context, i)
@ -181,14 +234,14 @@ impl JSError {
impl Error for JSError {}
fn format_source_loc(
script_name: &str,
file_name: &str,
line_number: i64,
column: i64,
column_number: i64,
) -> String {
// TODO match this style with how typescript displays errors.
let line_number = line_number + 1;
let column = column + 1;
format!("{}:{}:{}", script_name, line_number, column)
let line_number = line_number;
let column_number = column_number;
format!("{}:{}:{}", file_name, line_number, column_number)
}
impl fmt::Display for JSError {
@ -200,8 +253,8 @@ impl fmt::Display for JSError {
assert!(self.start_column.is_some());
let source_loc = format_source_loc(
script_resource_name,
self.line_number.unwrap() - 1,
self.start_column.unwrap() - 1,
self.line_number.unwrap(),
self.start_column.unwrap(),
);
write!(f, "{}", source_loc)?;
}