mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
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:
parent
5105c68399
commit
0ea6eb83a9
13 changed files with 271 additions and 192 deletions
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
12
cli/js/lib.deno.ns.d.ts
vendored
12
cli/js/lib.deno.ns.d.ts
vendored
|
@ -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}`);
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
})))
|
||||
}
|
||||
|
||||
|
|
|
@ -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');",
|
||||
|
|
13
cli/tests/error_024_stack_promise_all.ts
Normal file
13
cli/tests/error_024_stack_promise_all.ts
Normal 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;
|
||||
}
|
8
cli/tests/error_024_stack_promise_all.ts.out
Normal file
8
cli/tests/error_024_stack_promise_all.ts.out
Normal 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]
|
|
@ -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,
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue