summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2020-04-13 15:54:16 +0100
committerGitHub <noreply@github.com>2020-04-13 10:54:16 -0400
commit0ea6eb83a906bff543be4c3301f23444986b022b (patch)
tree923e5b1c7608839c9a7be545f8973ae751ee7e73
parent5105c6839904f35351481137160459fdc2edadd2 (diff)
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.
-rw-r--r--cli/diagnostics.rs8
-rw-r--r--cli/fmt_errors.rs47
-rw-r--r--cli/js/error_stack.ts55
-rw-r--r--cli/js/globals.ts12
-rw-r--r--cli/js/lib.deno.ns.d.ts12
-rw-r--r--cli/js/ops/errors.ts24
-rw-r--r--cli/js/tests/error_stack_test.ts36
-rw-r--r--cli/ops/errors.rs28
-rw-r--r--cli/source_maps.rs74
-rw-r--r--cli/tests/error_024_stack_promise_all.ts13
-rw-r--r--cli/tests/error_024_stack_promise_all.ts.out8
-rw-r--r--cli/tests/integration_tests.rs7
-rw-r--r--core/js_errors.rs137
13 files changed, 270 insertions, 191 deletions
diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs
index d73004b21..b0b284360 100644
--- a/cli/diagnostics.rs
+++ b/cli/diagnostics.rs
@@ -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),
)
}
}
diff --git a/cli/fmt_errors.rs b/cli/fmt_errors.rs
index 784bcc9c9..19e6616ca 100644
--- a/cli/fmt_errors.rs
+++ b/cli/fmt_errors.rs
@@ -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"
);
}
diff --git a/cli/js/error_stack.ts b/cli/js/error_stack.ts
index 43723dff6..e77f0865c 100644
--- a/cli/js/error_stack.ts
+++ b/cli/js/error_stack.ts
@@ -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");
diff --git a/cli/js/globals.ts b/cli/js/globals.ts
index 7e708d018..5a5f54ee1 100644
--- a/cli/js/globals.ts
+++ b/cli/js/globals.ts
@@ -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;
diff --git a/cli/js/lib.deno.ns.d.ts b/cli/js/lib.deno.ns.d.ts
index 75d10642b..550b6cffc 100644
--- a/cli/js/lib.deno.ns.d.ts
+++ b/cli/js/lib.deno.ns.d.ts
@@ -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}`);
*/
diff --git a/cli/js/ops/errors.ts b/cli/js/ops/errors.ts
index 5b65d102a..bd3e6d809 100644
--- a/cli/js/ops/errors.ts
+++ b/cli/js/ops/errors.ts
@@ -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,
};
}
diff --git a/cli/js/tests/error_stack_test.ts b/cli/js/tests/error_stack_test.ts
index 6868be215..83a2020ac 100644
--- a/cli/js/tests/error_stack_test.ts
+++ b/cli/js/tests/error_stack_test.ts
@@ -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);
});
diff --git a/cli/ops/errors.rs b/cli/ops/errors.rs
index 4d2ce1bef..2eab3a953 100644
--- a/cli/ops/errors.rs
+++ b/cli/ops/errors.rs
@@ -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,
})))
}
diff --git a/cli/source_maps.rs b/cli/source_maps.rs
index 2d442b823..90780e042 100644
--- a/cli/source_maps.rs
+++ b/cli/source_maps.rs
@@ -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');",
diff --git a/cli/tests/error_024_stack_promise_all.ts b/cli/tests/error_024_stack_promise_all.ts
new file mode 100644
index 000000000..5334e8d77
--- /dev/null
+++ b/cli/tests/error_024_stack_promise_all.ts
@@ -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;
+}
diff --git a/cli/tests/error_024_stack_promise_all.ts.out b/cli/tests/error_024_stack_promise_all.ts.out
new file mode 100644
index 000000000..38cb4ac31
--- /dev/null
+++ b/cli/tests/error_024_stack_promise_all.ts.out
@@ -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]
diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs
index d043958a9..905870c29 100644
--- a/cli/tests/integration_tests.rs
+++ b/cli/tests/integration_tests.rs
@@ -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,
diff --git a/core/js_errors.rs b/core/js_errors.rs
index 87f603a16..06a8d188f 100644
--- a/core/js_errors.rs
+++ b/core/js_errors.rs
@@ -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> =
- get_property(scope, context, call_site, "lineNumber")
+ let type_name: Option<v8::Local<v8::String>> =
+ get_property(scope, context, call_site, "typeName")
.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")
+ .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()
- .unwrap();
- let column_number = column_number.value() - 1;
- let file_name: Result<v8::Local<v8::String>, _> =
+ .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();
- 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")
+ .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();
- 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")
+ .try_into()
+ .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 is_constructor = is_constructor.is_true();
+ .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()
+ .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)?;
}