mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Process source maps in Rust instead of JS (#1280)
- Improves speed and binary size significantly. - Makes deno_last_exception() output a JSON structure. - Isolate::execute and Isolate::event_loop now return structured, mapped JSError objects on errors. - Removes libdeno functions: libdeno.setGlobalErrorHandler() libdeno.setPromiseRejectHandler() libdeno.setPromiseErrorExaminer() In collaboration with Ryan Dahl.
This commit is contained in:
parent
568ac0c902
commit
c113df1bb8
30 changed files with 854 additions and 807 deletions
4
BUILD.gn
4
BUILD.gn
|
@ -39,6 +39,8 @@ main_extern = [
|
||||||
"$rust_build:remove_dir_all",
|
"$rust_build:remove_dir_all",
|
||||||
"$rust_build:ring",
|
"$rust_build:ring",
|
||||||
"$rust_build:rustyline",
|
"$rust_build:rustyline",
|
||||||
|
"$rust_build:serde_json",
|
||||||
|
"$rust_build:source_map_mappings",
|
||||||
"$rust_build:tempfile",
|
"$rust_build:tempfile",
|
||||||
"$rust_build:tokio",
|
"$rust_build:tokio",
|
||||||
"$rust_build:tokio_executor",
|
"$rust_build:tokio_executor",
|
||||||
|
@ -84,7 +86,6 @@ ts_sources = [
|
||||||
"js/platform.ts",
|
"js/platform.ts",
|
||||||
"js/plugins.d.ts",
|
"js/plugins.d.ts",
|
||||||
"js/process.ts",
|
"js/process.ts",
|
||||||
"js/promise_util.ts",
|
|
||||||
"js/read_dir.ts",
|
"js/read_dir.ts",
|
||||||
"js/read_file.ts",
|
"js/read_file.ts",
|
||||||
"js/read_link.ts",
|
"js/read_link.ts",
|
||||||
|
@ -101,7 +102,6 @@ ts_sources = [
|
||||||
"js/types.ts",
|
"js/types.ts",
|
||||||
"js/url_search_params.ts",
|
"js/url_search_params.ts",
|
||||||
"js/util.ts",
|
"js/util.ts",
|
||||||
"js/v8_source_maps.ts",
|
|
||||||
"js/write_file.ts",
|
"js/write_file.ts",
|
||||||
"tsconfig.json",
|
"tsconfig.json",
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ rand = "=0.6.1"
|
||||||
remove_dir_all = "=0.5.1"
|
remove_dir_all = "=0.5.1"
|
||||||
rustyline = "=2.1.0"
|
rustyline = "=2.1.0"
|
||||||
ring = "=0.13.5"
|
ring = "=0.13.5"
|
||||||
|
serde_json = "1.0.33"
|
||||||
|
source-map-mappings = "0.5.0"
|
||||||
tempfile = "=3.0.5"
|
tempfile = "=3.0.5"
|
||||||
tokio = "=0.1.13"
|
tokio = "=0.1.13"
|
||||||
tokio-executor = "=0.1.5"
|
tokio-executor = "=0.1.5"
|
||||||
|
|
|
@ -1101,3 +1101,45 @@ rust_crate("tokio_process") {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rust_crate("vlq") {
|
||||||
|
source_root = "$registry_github/vlq-0.5.1/src/lib.rs"
|
||||||
|
}
|
||||||
|
|
||||||
|
rust_crate("source_map_mappings") {
|
||||||
|
source_root = "$registry_github/source-map-mappings-0.5.0/src/lib.rs"
|
||||||
|
extern = [
|
||||||
|
":rand",
|
||||||
|
":vlq",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
rust_crate("ryu") {
|
||||||
|
source_root = "$registry_github/ryu-0.2.7/src/lib.rs"
|
||||||
|
features = [ "small" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
rust_crate("serde") {
|
||||||
|
source_root = "$registry_github/serde-1.0.80/src/lib.rs"
|
||||||
|
features = [
|
||||||
|
"default",
|
||||||
|
"std",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
rust_crate("serde_json") {
|
||||||
|
source_root = "$registry_github/serde_json-1.0.33/src/lib.rs"
|
||||||
|
features = [
|
||||||
|
"arbitrary_precision",
|
||||||
|
"default",
|
||||||
|
"preserve_order",
|
||||||
|
"indexmap",
|
||||||
|
"raw_value",
|
||||||
|
]
|
||||||
|
extern = [
|
||||||
|
":indexmap",
|
||||||
|
":itoa",
|
||||||
|
":ryu",
|
||||||
|
":serde",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,8 @@ import * as ts from "typescript";
|
||||||
import { MediaType } from "gen/msg_generated";
|
import { MediaType } from "gen/msg_generated";
|
||||||
|
|
||||||
import { assetSourceCode } from "./assets";
|
import { assetSourceCode } from "./assets";
|
||||||
import { libdeno } from "./libdeno";
|
|
||||||
import * as os from "./os";
|
import * as os from "./os";
|
||||||
import { CodeProvider } from "./runner";
|
import { CodeProvider } from "./runner";
|
||||||
import { RawSourceMap } from "./types";
|
|
||||||
import { assert, log, notImplemented } from "./util";
|
import { assert, log, notImplemented } from "./util";
|
||||||
|
|
||||||
const EOL = "\n";
|
const EOL = "\n";
|
||||||
|
@ -71,7 +69,7 @@ export class ModuleMetaData implements ts.IScriptSnapshot {
|
||||||
public readonly mediaType: MediaType,
|
public readonly mediaType: MediaType,
|
||||||
public readonly sourceCode: SourceCode = "",
|
public readonly sourceCode: SourceCode = "",
|
||||||
public outputCode: OutputCode = "",
|
public outputCode: OutputCode = "",
|
||||||
public sourceMap: SourceMap | RawSourceMap = ""
|
public sourceMap: SourceMap = ""
|
||||||
) {
|
) {
|
||||||
if (outputCode !== "" || fileName.endsWith(".d.ts")) {
|
if (outputCode !== "" || fileName.endsWith(".d.ts")) {
|
||||||
this.scriptVersion = "1";
|
this.scriptVersion = "1";
|
||||||
|
@ -131,8 +129,6 @@ export class Compiler
|
||||||
ContainingFile,
|
ContainingFile,
|
||||||
Map<ModuleSpecifier, ModuleFileName>
|
Map<ModuleSpecifier, ModuleFileName>
|
||||||
>();
|
>();
|
||||||
// Keep track of state of the last module requested via `getGeneratedContents`
|
|
||||||
private _lastModule: ModuleMetaData | undefined;
|
|
||||||
// A reference to the log utility, so it can be monkey patched during testing
|
// A reference to the log utility, so it can be monkey patched during testing
|
||||||
private _log = log;
|
private _log = log;
|
||||||
// A map of module file names to module meta data
|
// A map of module file names to module meta data
|
||||||
|
@ -369,19 +365,15 @@ export class Compiler
|
||||||
moduleMetaData.outputCode = `${
|
moduleMetaData.outputCode = `${
|
||||||
outputFile.text
|
outputFile.text
|
||||||
}\n//# sourceURL=${fileName}`;
|
}\n//# sourceURL=${fileName}`;
|
||||||
moduleMetaData.sourceMap = JSON.parse(sourceMapFile.text);
|
moduleMetaData.sourceMap = sourceMapFile.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleMetaData.scriptVersion = "1";
|
moduleMetaData.scriptVersion = "1";
|
||||||
const sourceMap =
|
|
||||||
moduleMetaData.sourceMap === "string"
|
|
||||||
? moduleMetaData.sourceMap
|
|
||||||
: JSON.stringify(moduleMetaData.sourceMap);
|
|
||||||
this._os.codeCache(
|
this._os.codeCache(
|
||||||
fileName,
|
fileName,
|
||||||
sourceCode,
|
sourceCode,
|
||||||
moduleMetaData.outputCode,
|
moduleMetaData.outputCode,
|
||||||
sourceMap
|
moduleMetaData.sourceMap
|
||||||
);
|
);
|
||||||
return moduleMetaData.outputCode;
|
return moduleMetaData.outputCode;
|
||||||
}
|
}
|
||||||
|
@ -398,36 +390,10 @@ export class Compiler
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Given a fileName, return what was generated by the compiler. */
|
/** Given a fileName, return what was generated by the compiler. */
|
||||||
getGeneratedContents = (fileName: string): string | RawSourceMap => {
|
getGeneratedSourceMap(fileName: string): string {
|
||||||
this._log("compiler.getGeneratedContents", fileName);
|
const moduleMetaData = this._moduleMetaDataMap.get(fileName);
|
||||||
if (fileName === "gen/bundle/main.js") {
|
return moduleMetaData ? moduleMetaData.sourceMap : "";
|
||||||
assert(libdeno.mainSource.length > 0);
|
}
|
||||||
return libdeno.mainSource;
|
|
||||||
} else if (fileName === "main.js.map") {
|
|
||||||
return libdeno.mainSourceMap;
|
|
||||||
} else if (fileName === "deno_main.js") {
|
|
||||||
return "";
|
|
||||||
} else if (!fileName.endsWith(".map")) {
|
|
||||||
const moduleMetaData = this._moduleMetaDataMap.get(fileName);
|
|
||||||
if (!moduleMetaData) {
|
|
||||||
this._lastModule = undefined;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
this._lastModule = moduleMetaData;
|
|
||||||
return moduleMetaData.outputCode;
|
|
||||||
} else {
|
|
||||||
if (this._lastModule && this._lastModule.sourceMap) {
|
|
||||||
// Assuming the the map will always be asked for after the source
|
|
||||||
// code.
|
|
||||||
const { sourceMap } = this._lastModule;
|
|
||||||
this._lastModule = undefined;
|
|
||||||
return sourceMap;
|
|
||||||
} else {
|
|
||||||
// Errors thrown here are caught by source-map.
|
|
||||||
throw new Error(`Unable to find source map: "${fileName}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Get the output code for a module based on its filename. A call to
|
/** Get the output code for a module based on its filename. A call to
|
||||||
* `.getFilename()` should occur before attempting to get the output code as
|
* `.getFilename()` should occur before attempting to get the output code as
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
import { RawSourceMap } from "./types";
|
|
||||||
import { globalEval } from "./global_eval";
|
import { globalEval } from "./global_eval";
|
||||||
|
|
||||||
// The libdeno functions are moved so that users can't access them.
|
// The libdeno functions are moved so that users can't access them.
|
||||||
|
@ -39,9 +38,6 @@ interface Libdeno {
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
setPromiseErrorExaminer: (handler: () => boolean) => void;
|
setPromiseErrorExaminer: (handler: () => boolean) => void;
|
||||||
|
|
||||||
mainSource: string;
|
|
||||||
mainSourceMap: RawSourceMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const window = globalEval("this");
|
const window = globalEval("this");
|
||||||
|
|
33
js/main.ts
33
js/main.ts
|
@ -11,22 +11,9 @@ import { Runner } from "./runner";
|
||||||
import { libdeno } from "./libdeno";
|
import { libdeno } from "./libdeno";
|
||||||
import { args } from "./deno";
|
import { args } from "./deno";
|
||||||
import { sendSync, handleAsyncMsgFromRust } from "./dispatch";
|
import { sendSync, handleAsyncMsgFromRust } from "./dispatch";
|
||||||
import { promiseErrorExaminer, promiseRejectHandler } from "./promise_util";
|
|
||||||
import { replLoop } from "./repl";
|
import { replLoop } from "./repl";
|
||||||
import * as sourceMaps from "./v8_source_maps";
|
|
||||||
import { version } from "typescript";
|
import { version } from "typescript";
|
||||||
|
|
||||||
// Install the source maps handler and do some pre-calculations so all of it is
|
|
||||||
// available in the snapshot
|
|
||||||
const compiler = Compiler.instance();
|
|
||||||
sourceMaps.install({
|
|
||||||
installPrepareStackTrace: true,
|
|
||||||
getGeneratedContents: compiler.getGeneratedContents
|
|
||||||
});
|
|
||||||
const consumer = sourceMaps.loadConsumer("gen/bundle/main.js");
|
|
||||||
assert(consumer != null);
|
|
||||||
consumer!.computeColumnSpans();
|
|
||||||
|
|
||||||
function sendStart(): msg.StartRes {
|
function sendStart(): msg.StartRes {
|
||||||
const builder = flatbuffers.createBuilder();
|
const builder = flatbuffers.createBuilder();
|
||||||
msg.Start.startStart(builder);
|
msg.Start.startStart(builder);
|
||||||
|
@ -39,27 +26,9 @@ function sendStart(): msg.StartRes {
|
||||||
return startRes;
|
return startRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGlobalError(
|
|
||||||
message: string,
|
|
||||||
source: string,
|
|
||||||
lineno: number,
|
|
||||||
colno: number,
|
|
||||||
error: any // tslint:disable-line:no-any
|
|
||||||
) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
console.log(error.stack);
|
|
||||||
} else {
|
|
||||||
console.log(`Thrown: ${String(error)}`);
|
|
||||||
}
|
|
||||||
os.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tslint:disable-next-line:no-default-export */
|
/* tslint:disable-next-line:no-default-export */
|
||||||
export default function denoMain() {
|
export default function denoMain() {
|
||||||
libdeno.recv(handleAsyncMsgFromRust);
|
libdeno.recv(handleAsyncMsgFromRust);
|
||||||
libdeno.setGlobalErrorHandler(onGlobalError);
|
|
||||||
libdeno.setPromiseRejectHandler(promiseRejectHandler);
|
|
||||||
libdeno.setPromiseErrorExaminer(promiseErrorExaminer);
|
|
||||||
|
|
||||||
// First we send an empty "Start" message to let the privileged side know we
|
// First we send an empty "Start" message to let the privileged side know we
|
||||||
// are ready. The response should be a "StartRes" message containing the CLI
|
// are ready. The response should be a "StartRes" message containing the CLI
|
||||||
|
@ -68,6 +37,8 @@ export default function denoMain() {
|
||||||
|
|
||||||
setLogDebug(startResMsg.debugFlag());
|
setLogDebug(startResMsg.debugFlag());
|
||||||
|
|
||||||
|
const compiler = Compiler.instance();
|
||||||
|
|
||||||
// handle `--types`
|
// handle `--types`
|
||||||
if (startResMsg.typesFlag()) {
|
if (startResMsg.typesFlag()) {
|
||||||
const defaultLibFileName = compiler.getDefaultLibFileName();
|
const defaultLibFileName = compiler.getDefaultLibFileName();
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
|
||||||
import { PromiseRejectEvent } from "./libdeno";
|
|
||||||
|
|
||||||
/* tslint:disable-next-line:no-any */
|
|
||||||
const rejectMap = new Map<Promise<any>, string>();
|
|
||||||
// For uncaught promise rejection errors
|
|
||||||
|
|
||||||
/* tslint:disable-next-line:no-any */
|
|
||||||
const otherErrorMap = new Map<Promise<any>, string>();
|
|
||||||
// For reject after resolve / resolve after resolve errors
|
|
||||||
|
|
||||||
export function promiseRejectHandler(
|
|
||||||
error: Error | string,
|
|
||||||
event: PromiseRejectEvent,
|
|
||||||
/* tslint:disable-next-line:no-any */
|
|
||||||
promise: Promise<any>
|
|
||||||
) {
|
|
||||||
switch (event) {
|
|
||||||
case "RejectWithNoHandler":
|
|
||||||
rejectMap.set(promise, (error as Error).stack || "RejectWithNoHandler");
|
|
||||||
break;
|
|
||||||
case "HandlerAddedAfterReject":
|
|
||||||
rejectMap.delete(promise);
|
|
||||||
break;
|
|
||||||
case "ResolveAfterResolved":
|
|
||||||
// Should not warn. See #1272
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// error is string here
|
|
||||||
otherErrorMap.set(promise, `Promise warning: ${error as string}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true when continue, false to die on uncaught promise reject
|
|
||||||
export function promiseErrorExaminer(): boolean {
|
|
||||||
if (otherErrorMap.size > 0) {
|
|
||||||
for (const msg of otherErrorMap.values()) {
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
otherErrorMap.clear();
|
|
||||||
}
|
|
||||||
if (rejectMap.size > 0) {
|
|
||||||
for (const msg of rejectMap.values()) {
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
rejectMap.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -35,5 +35,4 @@ import "./text_encoding_test.ts";
|
||||||
import "./timers_test.ts";
|
import "./timers_test.ts";
|
||||||
import "./truncate_test.ts";
|
import "./truncate_test.ts";
|
||||||
import "./url_search_params_test.ts";
|
import "./url_search_params_test.ts";
|
||||||
import "./v8_source_maps_test.ts";
|
|
||||||
import "./write_file_test.ts";
|
import "./write_file_test.ts";
|
||||||
|
|
|
@ -1,285 +0,0 @@
|
||||||
// Copyright 2014 Evan Wallace
|
|
||||||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
|
||||||
// Originated from source-map-support but has been heavily modified for deno.
|
|
||||||
|
|
||||||
import { SourceMapConsumer, MappedPosition } from "source-map";
|
|
||||||
import { CallSite, RawSourceMap } from "./types";
|
|
||||||
import { atob } from "./text_encoding";
|
|
||||||
|
|
||||||
const consumers = new Map<string, SourceMapConsumer>();
|
|
||||||
|
|
||||||
interface Options {
|
|
||||||
// A callback the returns generated file contents.
|
|
||||||
getGeneratedContents: GetGeneratedContentsCallback;
|
|
||||||
// Usually set the following to true. Set to false for testing.
|
|
||||||
installPrepareStackTrace: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Position {
|
|
||||||
source: string; // Filename
|
|
||||||
column: number;
|
|
||||||
line: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetGeneratedContentsCallback = (fileName: string) => string | RawSourceMap;
|
|
||||||
|
|
||||||
let getGeneratedContents: GetGeneratedContentsCallback;
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function install(options: Options) {
|
|
||||||
getGeneratedContents = options.getGeneratedContents;
|
|
||||||
if (options.installPrepareStackTrace) {
|
|
||||||
Error.prepareStackTrace = prepareStackTraceWrapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function prepareStackTraceWrapper(
|
|
||||||
error: Error,
|
|
||||||
stack: CallSite[]
|
|
||||||
): string {
|
|
||||||
try {
|
|
||||||
return prepareStackTrace(error, stack);
|
|
||||||
} catch (prepareStackError) {
|
|
||||||
Error.prepareStackTrace = undefined;
|
|
||||||
console.log("=====Error inside of prepareStackTrace====");
|
|
||||||
console.log(prepareStackError.stack.toString());
|
|
||||||
console.log("=====Original error=======================");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function prepareStackTrace(error: Error, stack: CallSite[]): string {
|
|
||||||
const frames = stack.map(
|
|
||||||
frame => `\n at ${wrapCallSite(frame).toString()}`
|
|
||||||
);
|
|
||||||
return `${error.toString()}${frames.join("")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function wrapCallSite(frame: CallSite): CallSite {
|
|
||||||
if (frame.isNative()) {
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Most call sites will return the source file from getFileName(), but code
|
|
||||||
// passed to eval() ending in "//# sourceURL=..." will return the source file
|
|
||||||
// from getScriptNameOrSourceURL() instead
|
|
||||||
const source = frame.getFileName() || frame.getScriptNameOrSourceURL();
|
|
||||||
|
|
||||||
if (source) {
|
|
||||||
const line = frame.getLineNumber() || 0;
|
|
||||||
const column = (frame.getColumnNumber() || 1) - 1;
|
|
||||||
const position = mapSourcePosition({ source, line, column });
|
|
||||||
frame = cloneCallSite(frame);
|
|
||||||
Object.assign(frame, {
|
|
||||||
getFileName: () => position.source,
|
|
||||||
getLineNumber: () => position.line,
|
|
||||||
getColumnNumber: () => Number(position.column) + 1,
|
|
||||||
getScriptNameOrSourceURL: () => position.source,
|
|
||||||
toString: () => CallSiteToString(frame)
|
|
||||||
});
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Code called using eval() needs special handling
|
|
||||||
let origin = (frame.isEval() && frame.getEvalOrigin()) || undefined;
|
|
||||||
if (origin) {
|
|
||||||
origin = mapEvalOrigin(origin);
|
|
||||||
frame = cloneCallSite(frame);
|
|
||||||
Object.assign(frame, {
|
|
||||||
getEvalOrigin: () => origin,
|
|
||||||
toString: () => CallSiteToString(frame)
|
|
||||||
});
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we get here then we were unable to change the source position
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
function cloneCallSite(
|
|
||||||
frame: CallSite
|
|
||||||
// mixin: Partial<CallSite> & { toString: () => string }
|
|
||||||
): CallSite {
|
|
||||||
const obj = {} as CallSite;
|
|
||||||
const props = Object.getOwnPropertyNames(
|
|
||||||
Object.getPrototypeOf(frame)
|
|
||||||
) as Array<keyof CallSite>;
|
|
||||||
for (const name of props) {
|
|
||||||
obj[name] = /^(?:is|get)/.test(name)
|
|
||||||
? () => frame[name].call(frame)
|
|
||||||
: frame[name];
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Taken from source-map-support, original copied from V8's messages.js
|
|
||||||
// MIT License. Copyright (c) 2014 Evan Wallace
|
|
||||||
function CallSiteToString(frame: CallSite): string {
|
|
||||||
let fileLocation = "";
|
|
||||||
if (frame.isNative()) {
|
|
||||||
fileLocation = "native";
|
|
||||||
} else {
|
|
||||||
const fileName = frame.getScriptNameOrSourceURL();
|
|
||||||
if (!fileName && frame.isEval()) {
|
|
||||||
fileLocation = frame.getEvalOrigin() || "";
|
|
||||||
fileLocation += ", "; // Expecting source position to follow.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileName) {
|
|
||||||
fileLocation += fileName;
|
|
||||||
} else {
|
|
||||||
// Source code does not originate from a file and is not native, but we
|
|
||||||
// can still get the source position inside the source string, e.g. in
|
|
||||||
// an eval string.
|
|
||||||
fileLocation += "<anonymous>";
|
|
||||||
}
|
|
||||||
const lineNumber = frame.getLineNumber();
|
|
||||||
if (lineNumber != null) {
|
|
||||||
fileLocation += `:${lineNumber}`;
|
|
||||||
const columnNumber = frame.getColumnNumber();
|
|
||||||
if (columnNumber) {
|
|
||||||
fileLocation += `:${columnNumber}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let line = "";
|
|
||||||
const functionName = frame.getFunctionName();
|
|
||||||
let addSuffix = true;
|
|
||||||
const isConstructor = frame.isConstructor();
|
|
||||||
const isMethodCall = !(frame.isToplevel() || isConstructor);
|
|
||||||
if (isMethodCall) {
|
|
||||||
let typeName = frame.getTypeName();
|
|
||||||
// Fixes shim to be backward compatible with Node v0 to v4
|
|
||||||
if (typeName === "[object Object]") {
|
|
||||||
typeName = "null";
|
|
||||||
}
|
|
||||||
const methodName = frame.getMethodName();
|
|
||||||
if (functionName) {
|
|
||||||
if (typeName && functionName.indexOf(typeName) !== 0) {
|
|
||||||
line += `${typeName}.`;
|
|
||||||
}
|
|
||||||
line += functionName;
|
|
||||||
if (
|
|
||||||
methodName &&
|
|
||||||
functionName.indexOf("." + methodName) !==
|
|
||||||
functionName.length - methodName.length - 1
|
|
||||||
) {
|
|
||||||
line += ` [as ${methodName} ]`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
line += `${typeName}.${methodName || "<anonymous>"}`;
|
|
||||||
}
|
|
||||||
} else if (isConstructor) {
|
|
||||||
line += `new ${functionName || "<anonymous>"}`;
|
|
||||||
} else if (functionName) {
|
|
||||||
line += functionName;
|
|
||||||
} else {
|
|
||||||
line += fileLocation;
|
|
||||||
addSuffix = false;
|
|
||||||
}
|
|
||||||
if (addSuffix) {
|
|
||||||
line += ` (${fileLocation})`;
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regex for detecting source maps
|
|
||||||
const reSourceMap = /^data:application\/json[^,]+base64,/;
|
|
||||||
|
|
||||||
export function loadConsumer(source: string): SourceMapConsumer | null {
|
|
||||||
let consumer = consumers.get(source);
|
|
||||||
if (consumer == null) {
|
|
||||||
const code = getGeneratedContents(source);
|
|
||||||
if (!code) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (typeof code !== "string") {
|
|
||||||
throw new Error("expected string");
|
|
||||||
}
|
|
||||||
|
|
||||||
let sourceMappingURL = retrieveSourceMapURL(code);
|
|
||||||
if (!sourceMappingURL) {
|
|
||||||
throw Error("No source map?");
|
|
||||||
}
|
|
||||||
|
|
||||||
let sourceMapData: string | RawSourceMap;
|
|
||||||
if (reSourceMap.test(sourceMappingURL)) {
|
|
||||||
// Support source map URL as a data url
|
|
||||||
const rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(",") + 1);
|
|
||||||
sourceMapData = atob(rawData);
|
|
||||||
sourceMappingURL = source;
|
|
||||||
} else {
|
|
||||||
// TODO Support source map URLs relative to the source URL
|
|
||||||
// sourceMappingURL = supportRelativeURL(source, sourceMappingURL);
|
|
||||||
sourceMapData = getGeneratedContents(sourceMappingURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rawSourceMap =
|
|
||||||
typeof sourceMapData === "string"
|
|
||||||
? (JSON.parse(sourceMapData) as RawSourceMap)
|
|
||||||
: sourceMapData;
|
|
||||||
consumer = new SourceMapConsumer(rawSourceMap);
|
|
||||||
consumers.set(source, consumer);
|
|
||||||
}
|
|
||||||
return consumer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line:max-line-length
|
|
||||||
const sourceMapUrlRe = /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/)[ \t]*$)/gm;
|
|
||||||
|
|
||||||
function retrieveSourceMapURL(fileData: string): string | null {
|
|
||||||
// Keep executing the search to find the *last* sourceMappingURL to avoid
|
|
||||||
// picking up sourceMappingURLs from comments, strings, etc.
|
|
||||||
let lastMatch, match;
|
|
||||||
while ((match = sourceMapUrlRe.exec(fileData))) {
|
|
||||||
lastMatch = match;
|
|
||||||
}
|
|
||||||
if (!lastMatch) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return lastMatch[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mapSourcePosition(position: Position): MappedPosition {
|
|
||||||
const consumer = loadConsumer(position.source);
|
|
||||||
if (consumer == null) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
return consumer.originalPositionFor(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stackEvalRe = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/;
|
|
||||||
const nestedEvalRe = /^eval at ([^(]+) \((.+)\)$/;
|
|
||||||
|
|
||||||
// Parses code generated by FormatEvalOrigin(), a function inside V8:
|
|
||||||
// https://code.google.com/p/v8/source/browse/trunk/src/messages.js
|
|
||||||
function mapEvalOrigin(origin: string): string {
|
|
||||||
// Most eval() calls are in this format
|
|
||||||
let match = stackEvalRe.exec(origin);
|
|
||||||
if (match) {
|
|
||||||
const position = mapSourcePosition({
|
|
||||||
source: match[2],
|
|
||||||
line: Number(match[3]),
|
|
||||||
column: Number(match[4]) - 1
|
|
||||||
});
|
|
||||||
const pos = [
|
|
||||||
position.source,
|
|
||||||
position.line,
|
|
||||||
Number(position.column) + 1
|
|
||||||
].join(":");
|
|
||||||
return `eval at ${match[1]} (${pos})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse nested eval() calls using recursion
|
|
||||||
match = nestedEvalRe.exec(origin);
|
|
||||||
if (match) {
|
|
||||||
return `eval at ${match[1]} (${mapEvalOrigin(match[2])})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we still return useful information if we didn't find anything
|
|
||||||
return origin;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
|
||||||
import { test, assert, assertEqual } from "./test_util.ts";
|
|
||||||
|
|
||||||
// This test demonstrates a bug:
|
|
||||||
// https://github.com/denoland/deno/issues/808
|
|
||||||
test(function evalErrorFormatted() {
|
|
||||||
let err;
|
|
||||||
try {
|
|
||||||
eval("boom");
|
|
||||||
} catch (e) {
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
assert(!!err);
|
|
||||||
// tslint:disable-next-line:no-unused-expression
|
|
||||||
err.stack; // This would crash if err.stack is malformed
|
|
||||||
assertEqual(err.name, "ReferenceError");
|
|
||||||
});
|
|
|
@ -38,7 +38,7 @@ Deno* deno_new(deno_buf snapshot, deno_config config) {
|
||||||
if (!snapshot.data_ptr) {
|
if (!snapshot.data_ptr) {
|
||||||
// If no snapshot is provided, we initialize the context with empty
|
// If no snapshot is provided, we initialize the context with empty
|
||||||
// main source code and source maps.
|
// main source code and source maps.
|
||||||
deno::InitializeContext(isolate, context, "", "", "");
|
deno::InitializeContext(isolate, context, "", "");
|
||||||
}
|
}
|
||||||
d->context_.Reset(isolate, context);
|
d->context_.Reset(isolate, context);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ Deno* deno_new(deno_buf snapshot, deno_config config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Deno* deno_new_snapshotter(deno_config config, const char* js_filename,
|
Deno* deno_new_snapshotter(deno_config config, const char* js_filename,
|
||||||
const char* js_source, const char* source_map) {
|
const char* js_source) {
|
||||||
auto* creator = new v8::SnapshotCreator(deno::external_references);
|
auto* creator = new v8::SnapshotCreator(deno::external_references);
|
||||||
auto* isolate = creator->GetIsolate();
|
auto* isolate = creator->GetIsolate();
|
||||||
auto* d = new deno::DenoIsolate(deno::empty_buf, config);
|
auto* d = new deno::DenoIsolate(deno::empty_buf, config);
|
||||||
|
@ -61,8 +61,7 @@ Deno* deno_new_snapshotter(deno_config config, const char* js_filename,
|
||||||
creator->SetDefaultContext(context,
|
creator->SetDefaultContext(context,
|
||||||
v8::SerializeInternalFieldsCallback(
|
v8::SerializeInternalFieldsCallback(
|
||||||
deno::SerializeInternalFields, nullptr));
|
deno::SerializeInternalFields, nullptr));
|
||||||
deno::InitializeContext(isolate, context, js_filename, js_source,
|
deno::InitializeContext(isolate, context, js_filename, js_source);
|
||||||
source_map);
|
|
||||||
}
|
}
|
||||||
return reinterpret_cast<Deno*>(d);
|
return reinterpret_cast<Deno*>(d);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +95,11 @@ void deno_set_v8_flags(int* argc, char** argv) {
|
||||||
|
|
||||||
const char* deno_last_exception(Deno* d_) {
|
const char* deno_last_exception(Deno* d_) {
|
||||||
auto* d = unwrap(d_);
|
auto* d = unwrap(d_);
|
||||||
return d->last_exception_.c_str();
|
if (d->last_exception_.length() > 0) {
|
||||||
|
return d->last_exception_.c_str();
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int deno_execute(Deno* d_, void* user_data, const char* js_filename,
|
int deno_execute(Deno* d_, void* user_data, const char* js_filename,
|
||||||
|
@ -154,31 +157,19 @@ int deno_respond(Deno* d_, void* user_data, int32_t req_id, deno_buf buf) {
|
||||||
|
|
||||||
void deno_check_promise_errors(Deno* d_) {
|
void deno_check_promise_errors(Deno* d_) {
|
||||||
auto* d = unwrap(d_);
|
auto* d = unwrap(d_);
|
||||||
if (d->pending_promise_events_ > 0) {
|
if (d->pending_promise_map_.size() > 0) {
|
||||||
auto* isolate = d->isolate_;
|
auto* isolate = d->isolate_;
|
||||||
v8::Locker locker(isolate);
|
v8::Locker locker(isolate);
|
||||||
v8::Isolate::Scope isolate_scope(isolate);
|
v8::Isolate::Scope isolate_scope(isolate);
|
||||||
v8::HandleScope handle_scope(isolate);
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
|
||||||
auto context = d->context_.Get(d->isolate_);
|
auto context = d->context_.Get(d->isolate_);
|
||||||
v8::Context::Scope context_scope(context);
|
v8::Context::Scope context_scope(context);
|
||||||
|
|
||||||
v8::TryCatch try_catch(d->isolate_);
|
auto it = d->pending_promise_map_.begin();
|
||||||
auto promise_error_examiner_ = d->promise_error_examiner_.Get(d->isolate_);
|
while (it != d->pending_promise_map_.end()) {
|
||||||
if (promise_error_examiner_.IsEmpty()) {
|
auto error = it->second.Get(isolate);
|
||||||
d->last_exception_ =
|
deno::HandleException(context, error);
|
||||||
"libdeno.setPromiseErrorExaminer has not been called.";
|
it = d->pending_promise_map_.erase(it);
|
||||||
return;
|
|
||||||
}
|
|
||||||
v8::Local<v8::Value> args[0];
|
|
||||||
auto result = promise_error_examiner_->Call(context->Global(), 0, args);
|
|
||||||
if (try_catch.HasCaught()) {
|
|
||||||
deno::HandleException(context, try_catch.Exception());
|
|
||||||
}
|
|
||||||
d->pending_promise_events_ = 0; // reset
|
|
||||||
if (!result->BooleanValue(context).FromJust()) {
|
|
||||||
// Has uncaught promise reject error, exiting...
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,95 +72,94 @@ static inline v8::Local<v8::String> v8_str(const char* x) {
|
||||||
.ToLocalChecked();
|
.ToLocalChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleExceptionStr(v8::Local<v8::Context> context,
|
std::string EncodeExceptionAsJSON(v8::Local<v8::Context> context,
|
||||||
v8::Local<v8::Value> exception,
|
v8::Local<v8::Value> exception) {
|
||||||
std::string* exception_str) {
|
|
||||||
auto* isolate = context->GetIsolate();
|
auto* isolate = context->GetIsolate();
|
||||||
DenoIsolate* d = FromIsolate(isolate);
|
|
||||||
|
|
||||||
v8::HandleScope handle_scope(isolate);
|
v8::HandleScope handle_scope(isolate);
|
||||||
v8::Context::Scope context_scope(context);
|
v8::Context::Scope context_scope(context);
|
||||||
|
|
||||||
auto message = v8::Exception::CreateMessage(isolate, exception);
|
auto message = v8::Exception::CreateMessage(isolate, exception);
|
||||||
auto stack_trace = message->GetStackTrace();
|
auto stack_trace = message->GetStackTrace();
|
||||||
auto line =
|
|
||||||
v8::Integer::New(isolate, message->GetLineNumber(context).FromJust());
|
|
||||||
auto column =
|
|
||||||
v8::Integer::New(isolate, message->GetStartColumn(context).FromJust());
|
|
||||||
|
|
||||||
auto global_error_handler_ = d->global_error_handler_.Get(isolate);
|
// Encode the exception into a JS object, which we will then turn into JSON.
|
||||||
|
auto json_obj = v8::Object::New(isolate);
|
||||||
|
|
||||||
if (!global_error_handler_.IsEmpty()) {
|
auto exception_str = exception->ToString(context).ToLocalChecked();
|
||||||
// global_error_handler_ is set so we try to handle the exception in
|
// Alternate and very similar string. Not sure which is appropriate.
|
||||||
// javascript.
|
// auto exception_str = message->Get();
|
||||||
v8::Local<v8::Value> args[5];
|
CHECK(json_obj->Set(context, v8_str("message"), exception_str).FromJust());
|
||||||
args[0] = exception->ToString(context).ToLocalChecked();
|
|
||||||
args[1] = message->GetScriptResourceName();
|
|
||||||
args[2] = line;
|
|
||||||
args[3] = column;
|
|
||||||
args[4] = exception;
|
|
||||||
global_error_handler_->Call(context->Global(), 5, args);
|
|
||||||
/* message, source, lineno, colno, error */
|
|
||||||
|
|
||||||
return;
|
v8::Local<v8::Array> frames;
|
||||||
}
|
|
||||||
|
|
||||||
char buf[12 * 1024];
|
|
||||||
if (!stack_trace.IsEmpty()) {
|
if (!stack_trace.IsEmpty()) {
|
||||||
// No javascript error handler, but we do have a stack trace. Format it
|
uint32_t count = static_cast<uint32_t>(stack_trace->GetFrameCount());
|
||||||
// into a string and add to last_exception_.
|
frames = v8::Array::New(isolate, count);
|
||||||
std::string msg;
|
|
||||||
v8::String::Utf8Value exceptionStr(isolate, exception);
|
|
||||||
msg += ToCString(exceptionStr);
|
|
||||||
msg += "\n";
|
|
||||||
|
|
||||||
for (int i = 0; i < stack_trace->GetFrameCount(); ++i) {
|
for (uint32_t i = 0; i < count; ++i) {
|
||||||
auto frame = stack_trace->GetFrame(isolate, i);
|
auto frame = stack_trace->GetFrame(isolate, i);
|
||||||
v8::String::Utf8Value script_name(isolate, frame->GetScriptName());
|
auto frame_obj = v8::Object::New(isolate);
|
||||||
int l = frame->GetLineNumber();
|
CHECK(frames->Set(context, i, frame_obj).FromJust());
|
||||||
int c = frame->GetColumn();
|
auto line = v8::Integer::New(isolate, frame->GetLineNumber());
|
||||||
snprintf(buf, sizeof(buf), "%s %d:%d\n", ToCString(script_name), l, c);
|
auto column = v8::Integer::New(isolate, frame->GetColumn());
|
||||||
msg += buf;
|
CHECK(frame_obj->Set(context, v8_str("line"), line).FromJust());
|
||||||
|
CHECK(frame_obj->Set(context, v8_str("column"), column).FromJust());
|
||||||
|
CHECK(frame_obj
|
||||||
|
->Set(context, v8_str("functionName"), frame->GetFunctionName())
|
||||||
|
.FromJust());
|
||||||
|
CHECK(frame_obj
|
||||||
|
->Set(context, v8_str("scriptName"),
|
||||||
|
frame->GetScriptNameOrSourceURL())
|
||||||
|
.FromJust());
|
||||||
|
CHECK(frame_obj
|
||||||
|
->Set(context, v8_str("isEval"),
|
||||||
|
v8::Boolean::New(isolate, frame->IsEval()))
|
||||||
|
.FromJust());
|
||||||
|
CHECK(frame_obj
|
||||||
|
->Set(context, v8_str("isConstructor"),
|
||||||
|
v8::Boolean::New(isolate, frame->IsConstructor()))
|
||||||
|
.FromJust());
|
||||||
|
CHECK(frame_obj
|
||||||
|
->Set(context, v8_str("isWasm"),
|
||||||
|
v8::Boolean::New(isolate, frame->IsWasm()))
|
||||||
|
.FromJust());
|
||||||
}
|
}
|
||||||
*exception_str += msg;
|
|
||||||
} else {
|
} else {
|
||||||
// No javascript error handler, no stack trace. Format the little info we
|
// No stack trace. We only have one stack frame of info..
|
||||||
// have into a string and add to last_exception_.
|
frames = v8::Array::New(isolate, 1);
|
||||||
v8::String::Utf8Value exceptionStr(isolate, exception);
|
|
||||||
v8::String::Utf8Value script_name(isolate,
|
auto frame_obj = v8::Object::New(isolate);
|
||||||
message->GetScriptResourceName());
|
CHECK(frames->Set(context, 0, frame_obj).FromJust());
|
||||||
v8::String::Utf8Value line_str(isolate, line);
|
|
||||||
v8::String::Utf8Value col_str(isolate, column);
|
auto line =
|
||||||
snprintf(buf, sizeof(buf), "%s\n%s %s:%s\n", ToCString(exceptionStr),
|
v8::Integer::New(isolate, message->GetLineNumber(context).FromJust());
|
||||||
ToCString(script_name), ToCString(line_str), ToCString(col_str));
|
auto column =
|
||||||
*exception_str += buf;
|
v8::Integer::New(isolate, message->GetStartColumn(context).FromJust());
|
||||||
|
|
||||||
|
CHECK(frame_obj->Set(context, v8_str("line"), line).FromJust());
|
||||||
|
CHECK(frame_obj->Set(context, v8_str("column"), column).FromJust());
|
||||||
|
CHECK(frame_obj
|
||||||
|
->Set(context, v8_str("scriptName"),
|
||||||
|
message->GetScriptResourceName())
|
||||||
|
.FromJust());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CHECK(json_obj->Set(context, v8_str("frames"), frames).FromJust());
|
||||||
|
|
||||||
|
auto json_string = v8::JSON::Stringify(context, json_obj).ToLocalChecked();
|
||||||
|
v8::String::Utf8Value json_string_(isolate, json_string);
|
||||||
|
return std::string(ToCString(json_string_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleException(v8::Local<v8::Context> context,
|
void HandleException(v8::Local<v8::Context> context,
|
||||||
v8::Local<v8::Value> exception) {
|
v8::Local<v8::Value> exception) {
|
||||||
v8::Isolate* isolate = context->GetIsolate();
|
v8::Isolate* isolate = context->GetIsolate();
|
||||||
DenoIsolate* d = FromIsolate(isolate);
|
DenoIsolate* d = FromIsolate(isolate);
|
||||||
std::string exception_str;
|
std::string json_str = EncodeExceptionAsJSON(context, exception);
|
||||||
HandleExceptionStr(context, exception, &exception_str);
|
|
||||||
if (d != nullptr) {
|
if (d != nullptr) {
|
||||||
d->last_exception_ = exception_str;
|
d->last_exception_ = json_str;
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Pre-Deno Exception " << exception_str << std::endl;
|
// This shouldn't happen in normal circumstances. Added for debugging.
|
||||||
exit(1);
|
std::cerr << "Pre-Deno Exception " << json_str << std::endl;
|
||||||
}
|
CHECK(false);
|
||||||
}
|
|
||||||
|
|
||||||
const char* PromiseRejectStr(enum v8::PromiseRejectEvent e) {
|
|
||||||
switch (e) {
|
|
||||||
case v8::PromiseRejectEvent::kPromiseRejectWithNoHandler:
|
|
||||||
return "RejectWithNoHandler";
|
|
||||||
case v8::PromiseRejectEvent::kPromiseHandlerAddedAfterReject:
|
|
||||||
return "HandlerAddedAfterReject";
|
|
||||||
case v8::PromiseRejectEvent::kPromiseResolveAfterResolved:
|
|
||||||
return "ResolveAfterResolved";
|
|
||||||
case v8::PromiseRejectEvent::kPromiseRejectAfterResolved:
|
|
||||||
return "RejectAfterResolved";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,40 +168,35 @@ void PromiseRejectCallback(v8::PromiseRejectMessage promise_reject_message) {
|
||||||
DenoIsolate* d = static_cast<DenoIsolate*>(isolate->GetData(0));
|
DenoIsolate* d = static_cast<DenoIsolate*>(isolate->GetData(0));
|
||||||
DCHECK_EQ(d->isolate_, isolate);
|
DCHECK_EQ(d->isolate_, isolate);
|
||||||
v8::HandleScope handle_scope(d->isolate_);
|
v8::HandleScope handle_scope(d->isolate_);
|
||||||
auto exception = promise_reject_message.GetValue();
|
auto error = promise_reject_message.GetValue();
|
||||||
auto context = d->context_.Get(d->isolate_);
|
auto context = d->context_.Get(d->isolate_);
|
||||||
auto promise = promise_reject_message.GetPromise();
|
auto promise = promise_reject_message.GetPromise();
|
||||||
auto event = promise_reject_message.GetEvent();
|
|
||||||
|
|
||||||
v8::Context::Scope context_scope(context);
|
v8::Context::Scope context_scope(context);
|
||||||
auto promise_reject_handler = d->promise_reject_handler_.Get(isolate);
|
|
||||||
|
|
||||||
if (!promise_reject_handler.IsEmpty()) {
|
int promise_id = promise->GetIdentityHash();
|
||||||
v8::Local<v8::Value> args[3];
|
switch (promise_reject_message.GetEvent()) {
|
||||||
args[1] = v8_str(PromiseRejectStr(event));
|
case v8::kPromiseRejectWithNoHandler:
|
||||||
args[2] = promise;
|
// Insert the error into the pending_promise_map_ using the promise's id
|
||||||
/* error, event, promise */
|
// as the key.
|
||||||
if (event == v8::PromiseRejectEvent::kPromiseRejectWithNoHandler) {
|
d->pending_promise_map_.emplace(std::piecewise_construct,
|
||||||
d->pending_promise_events_++;
|
std::make_tuple(promise_id),
|
||||||
// exception only valid for kPromiseRejectWithNoHandler
|
std::make_tuple(d->isolate_, error));
|
||||||
args[0] = exception;
|
break;
|
||||||
} else if (event ==
|
|
||||||
v8::PromiseRejectEvent::kPromiseHandlerAddedAfterReject) {
|
case v8::kPromiseHandlerAddedAfterReject:
|
||||||
d->pending_promise_events_--; // unhandled event cancelled
|
d->pending_promise_map_.erase(promise_id);
|
||||||
if (d->pending_promise_events_ < 0) {
|
break;
|
||||||
d->pending_promise_events_ = 0;
|
|
||||||
}
|
case v8::kPromiseRejectAfterResolved:
|
||||||
// Placeholder, not actually used
|
break;
|
||||||
args[0] = v8_str("Promise handler added");
|
|
||||||
} else if (event == v8::PromiseRejectEvent::kPromiseResolveAfterResolved) {
|
case v8::kPromiseResolveAfterResolved:
|
||||||
d->pending_promise_events_++;
|
// Should not warn. See #1272
|
||||||
args[0] = v8_str("Promise resolved after resolved");
|
break;
|
||||||
} else if (event == v8::PromiseRejectEvent::kPromiseRejectAfterResolved) {
|
|
||||||
d->pending_promise_events_++;
|
default:
|
||||||
args[0] = v8_str("Promise rejected after resolved");
|
CHECK(false && "unreachable");
|
||||||
}
|
|
||||||
promise_reject_handler->Call(context->Global(), 3, args);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,69 +353,6 @@ void Shared(v8::Local<v8::Name> property,
|
||||||
info.GetReturnValue().Set(ab);
|
info.GetReturnValue().Set(ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the global error handler.
|
|
||||||
void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
||||||
v8::Isolate* isolate = args.GetIsolate();
|
|
||||||
DenoIsolate* d = FromIsolate(isolate);
|
|
||||||
DCHECK_EQ(d->isolate_, isolate);
|
|
||||||
|
|
||||||
v8::HandleScope handle_scope(isolate);
|
|
||||||
|
|
||||||
if (!d->global_error_handler_.IsEmpty()) {
|
|
||||||
isolate->ThrowException(
|
|
||||||
v8_str("libdeno.setGlobalErrorHandler already called."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v8::Local<v8::Value> v = args[0];
|
|
||||||
CHECK(v->IsFunction());
|
|
||||||
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
|
|
||||||
|
|
||||||
d->global_error_handler_.Reset(isolate, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the promise uncaught reject handler
|
|
||||||
void SetPromiseRejectHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
||||||
v8::Isolate* isolate = args.GetIsolate();
|
|
||||||
DenoIsolate* d = FromIsolate(isolate);
|
|
||||||
DCHECK_EQ(d->isolate_, isolate);
|
|
||||||
|
|
||||||
v8::HandleScope handle_scope(isolate);
|
|
||||||
|
|
||||||
if (!d->promise_reject_handler_.IsEmpty()) {
|
|
||||||
isolate->ThrowException(
|
|
||||||
v8_str("libdeno.setPromiseRejectHandler already called."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v8::Local<v8::Value> v = args[0];
|
|
||||||
CHECK(v->IsFunction());
|
|
||||||
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
|
|
||||||
|
|
||||||
d->promise_reject_handler_.Reset(isolate, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the promise uncaught reject handler
|
|
||||||
void SetPromiseErrorExaminer(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
||||||
v8::Isolate* isolate = args.GetIsolate();
|
|
||||||
DenoIsolate* d = FromIsolate(isolate);
|
|
||||||
DCHECK_EQ(d->isolate_, isolate);
|
|
||||||
|
|
||||||
v8::HandleScope handle_scope(isolate);
|
|
||||||
|
|
||||||
if (!d->promise_error_examiner_.IsEmpty()) {
|
|
||||||
isolate->ThrowException(
|
|
||||||
v8_str("libdeno.setPromiseErrorExaminer already called."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v8::Local<v8::Value> v = args[0];
|
|
||||||
CHECK(v->IsFunction());
|
|
||||||
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
|
|
||||||
|
|
||||||
d->promise_error_examiner_.Reset(isolate, func);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ExecuteV8StringSource(v8::Local<v8::Context> context,
|
bool ExecuteV8StringSource(v8::Local<v8::Context> context,
|
||||||
const char* js_filename,
|
const char* js_filename,
|
||||||
v8::Local<v8::String> source) {
|
v8::Local<v8::String> source) {
|
||||||
|
@ -466,8 +397,7 @@ bool Execute(v8::Local<v8::Context> context, const char* js_filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
|
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
|
||||||
const char* js_filename, const char* js_source,
|
const char* js_filename, const char* js_source) {
|
||||||
const char* source_map) {
|
|
||||||
CHECK_NE(js_source, nullptr);
|
CHECK_NE(js_source, nullptr);
|
||||||
CHECK_NE(js_filename, nullptr);
|
CHECK_NE(js_filename, nullptr);
|
||||||
v8::HandleScope handle_scope(isolate);
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
@ -493,61 +423,8 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
|
||||||
CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared)
|
CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared)
|
||||||
.FromJust());
|
.FromJust());
|
||||||
|
|
||||||
auto set_global_error_handler_tmpl =
|
|
||||||
v8::FunctionTemplate::New(isolate, SetGlobalErrorHandler);
|
|
||||||
auto set_global_error_handler_val =
|
|
||||||
set_global_error_handler_tmpl->GetFunction(context).ToLocalChecked();
|
|
||||||
CHECK(deno_val
|
|
||||||
->Set(context, deno::v8_str("setGlobalErrorHandler"),
|
|
||||||
set_global_error_handler_val)
|
|
||||||
.FromJust());
|
|
||||||
|
|
||||||
auto set_promise_reject_handler_tmpl =
|
|
||||||
v8::FunctionTemplate::New(isolate, SetPromiseRejectHandler);
|
|
||||||
auto set_promise_reject_handler_val =
|
|
||||||
set_promise_reject_handler_tmpl->GetFunction(context).ToLocalChecked();
|
|
||||||
CHECK(deno_val
|
|
||||||
->Set(context, deno::v8_str("setPromiseRejectHandler"),
|
|
||||||
set_promise_reject_handler_val)
|
|
||||||
.FromJust());
|
|
||||||
|
|
||||||
auto set_promise_error_examiner_tmpl =
|
|
||||||
v8::FunctionTemplate::New(isolate, SetPromiseErrorExaminer);
|
|
||||||
auto set_promise_error_examiner_val =
|
|
||||||
set_promise_error_examiner_tmpl->GetFunction(context).ToLocalChecked();
|
|
||||||
CHECK(deno_val
|
|
||||||
->Set(context, deno::v8_str("setPromiseErrorExaminer"),
|
|
||||||
set_promise_error_examiner_val)
|
|
||||||
.FromJust());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
if (source_map != nullptr) {
|
|
||||||
v8::TryCatch try_catch(isolate);
|
|
||||||
v8::ScriptOrigin origin(v8_str("set_source_map.js"));
|
|
||||||
std::string source_map_parens =
|
|
||||||
std::string("(") + std::string(source_map) + std::string(")");
|
|
||||||
auto source_map_v8_str = deno::v8_str(source_map_parens.c_str());
|
|
||||||
auto script = v8::Script::Compile(context, source_map_v8_str, &origin);
|
|
||||||
if (script.IsEmpty()) {
|
|
||||||
DCHECK(try_catch.HasCaught());
|
|
||||||
HandleException(context, try_catch.Exception());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto source_map_obj = script.ToLocalChecked()->Run(context);
|
|
||||||
if (source_map_obj.IsEmpty()) {
|
|
||||||
DCHECK(try_catch.HasCaught());
|
|
||||||
HandleException(context, try_catch.Exception());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CHECK(deno_val
|
|
||||||
->Set(context, deno::v8_str("mainSourceMap"),
|
|
||||||
source_map_obj.ToLocalChecked())
|
|
||||||
.FromJust());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto source = deno::v8_str(js_source);
|
auto source = deno::v8_str(js_source);
|
||||||
CHECK(
|
|
||||||
deno_val->Set(context, deno::v8_str("mainSource"), source).FromJust());
|
|
||||||
|
|
||||||
bool r = deno::ExecuteV8StringSource(context, js_filename, source);
|
bool r = deno::ExecuteV8StringSource(context, js_filename, source);
|
||||||
CHECK(r);
|
CHECK(r);
|
||||||
|
@ -558,10 +435,11 @@ void DenoIsolate::AddIsolate(v8::Isolate* isolate) {
|
||||||
isolate_ = isolate;
|
isolate_ = isolate;
|
||||||
// Leaving this code here because it will probably be useful later on, but
|
// Leaving this code here because it will probably be useful later on, but
|
||||||
// disabling it now as I haven't got tests for the desired behavior.
|
// disabling it now as I haven't got tests for the desired behavior.
|
||||||
// d->isolate->SetCaptureStackTraceForUncaughtExceptions(true);
|
|
||||||
// d->isolate->SetAbortOnUncaughtExceptionCallback(AbortOnUncaughtExceptionCallback);
|
// d->isolate->SetAbortOnUncaughtExceptionCallback(AbortOnUncaughtExceptionCallback);
|
||||||
// d->isolate->AddMessageListener(MessageCallback2);
|
// d->isolate->AddMessageListener(MessageCallback2);
|
||||||
// d->isolate->SetFatalErrorHandler(FatalErrorCallback2);
|
// d->isolate->SetFatalErrorHandler(FatalErrorCallback2);
|
||||||
|
isolate_->SetCaptureStackTraceForUncaughtExceptions(
|
||||||
|
true, 10, v8::StackTrace::kDetailed);
|
||||||
isolate_->SetPromiseRejectCallback(deno::PromiseRejectCallback);
|
isolate_->SetPromiseRejectCallback(deno::PromiseRejectCallback);
|
||||||
isolate_->SetData(0, this);
|
isolate_->SetData(0, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ typedef struct {
|
||||||
Deno* deno_new(deno_buf snapshot, deno_config config);
|
Deno* deno_new(deno_buf snapshot, deno_config config);
|
||||||
|
|
||||||
Deno* deno_new_snapshotter(deno_config config, const char* js_filename,
|
Deno* deno_new_snapshotter(deno_config config, const char* js_filename,
|
||||||
const char* js_source, const char* source_map);
|
const char* js_source);
|
||||||
|
|
||||||
// Generate a snapshot. The resulting buf can be used with deno_new.
|
// Generate a snapshot. The resulting buf can be used with deno_new.
|
||||||
// The caller must free the returned data by calling delete[] buf.data_ptr.
|
// The caller must free the returned data by calling delete[] buf.data_ptr.
|
||||||
|
@ -48,6 +48,12 @@ void deno_delete(Deno* d);
|
||||||
// Returns false on error.
|
// Returns false on error.
|
||||||
// Get error text with deno_last_exception().
|
// Get error text with deno_last_exception().
|
||||||
// 0 = fail, 1 = success
|
// 0 = fail, 1 = success
|
||||||
|
//
|
||||||
|
// TODO change return value to be const char*. On success the return
|
||||||
|
// value is nullptr, on failure it is the JSON exception text that
|
||||||
|
// is returned by deno_last_exception(). Remove deno_last_exception().
|
||||||
|
// The return string is valid until the next execution of deno_execute or
|
||||||
|
// deno_respond (as deno_last_exception is now).
|
||||||
int deno_execute(Deno* d, void* user_data, const char* js_filename,
|
int deno_execute(Deno* d, void* user_data, const char* js_filename,
|
||||||
const char* js_source);
|
const char* js_source);
|
||||||
|
|
||||||
|
@ -69,6 +75,12 @@ int deno_execute(Deno* d, void* user_data, const char* js_filename,
|
||||||
//
|
//
|
||||||
// A non-zero return value, means a JS exception was encountered during the
|
// A non-zero return value, means a JS exception was encountered during the
|
||||||
// libdeno.recv() callback. Check deno_last_exception() for exception text.
|
// libdeno.recv() callback. Check deno_last_exception() for exception text.
|
||||||
|
//
|
||||||
|
// TODO change return value to be const char*. On success the return
|
||||||
|
// value is nullptr, on failure it is the JSON exception text that
|
||||||
|
// is returned by deno_last_exception(). Remove deno_last_exception().
|
||||||
|
// The return string is valid until the next execution of deno_execute or
|
||||||
|
// deno_respond (as deno_last_exception is now).
|
||||||
int deno_respond(Deno* d, void* user_data, int32_t req_id, deno_buf buf);
|
int deno_respond(Deno* d, void* user_data, int32_t req_id, deno_buf buf);
|
||||||
|
|
||||||
void deno_check_promise_errors(Deno* d);
|
void deno_check_promise_errors(Deno* d);
|
||||||
|
|
|
@ -19,7 +19,6 @@ class DenoIsolate {
|
||||||
current_args_(nullptr),
|
current_args_(nullptr),
|
||||||
snapshot_creator_(nullptr),
|
snapshot_creator_(nullptr),
|
||||||
global_import_buf_ptr_(nullptr),
|
global_import_buf_ptr_(nullptr),
|
||||||
pending_promise_events_(0),
|
|
||||||
recv_cb_(config.recv_cb),
|
recv_cb_(config.recv_cb),
|
||||||
next_req_id_(0),
|
next_req_id_(0),
|
||||||
user_data_(nullptr) {
|
user_data_(nullptr) {
|
||||||
|
@ -47,13 +46,13 @@ class DenoIsolate {
|
||||||
const v8::FunctionCallbackInfo<v8::Value>* current_args_;
|
const v8::FunctionCallbackInfo<v8::Value>* current_args_;
|
||||||
v8::SnapshotCreator* snapshot_creator_;
|
v8::SnapshotCreator* snapshot_creator_;
|
||||||
void* global_import_buf_ptr_;
|
void* global_import_buf_ptr_;
|
||||||
int32_t pending_promise_events_;
|
|
||||||
deno_recv_cb recv_cb_;
|
deno_recv_cb recv_cb_;
|
||||||
int32_t next_req_id_;
|
int32_t next_req_id_;
|
||||||
void* user_data_;
|
void* user_data_;
|
||||||
|
|
||||||
v8::Persistent<v8::Context> context_;
|
v8::Persistent<v8::Context> context_;
|
||||||
std::map<int32_t, v8::Persistent<v8::Value>> async_data_map_;
|
std::map<int32_t, v8::Persistent<v8::Value>> async_data_map_;
|
||||||
|
std::map<int, v8::Persistent<v8::Value>> pending_promise_map_;
|
||||||
std::string last_exception_;
|
std::string last_exception_;
|
||||||
v8::Persistent<v8::Function> recv_;
|
v8::Persistent<v8::Function> recv_;
|
||||||
v8::Persistent<v8::Function> global_error_handler_;
|
v8::Persistent<v8::Function> global_error_handler_;
|
||||||
|
@ -91,26 +90,16 @@ void Recv(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
void Send(const v8::FunctionCallbackInfo<v8::Value>& args);
|
void Send(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
void Shared(v8::Local<v8::Name> property,
|
void Shared(v8::Local<v8::Name> property,
|
||||||
const v8::PropertyCallbackInfo<v8::Value>& info);
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||||
void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void SetPromiseRejectHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void SetPromiseErrorExaminer(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
static intptr_t external_references[] = {
|
static intptr_t external_references[] = {
|
||||||
reinterpret_cast<intptr_t>(Print),
|
reinterpret_cast<intptr_t>(Print), reinterpret_cast<intptr_t>(Recv),
|
||||||
reinterpret_cast<intptr_t>(Recv),
|
reinterpret_cast<intptr_t>(Send), reinterpret_cast<intptr_t>(Shared), 0};
|
||||||
reinterpret_cast<intptr_t>(Send),
|
|
||||||
reinterpret_cast<intptr_t>(Shared),
|
|
||||||
reinterpret_cast<intptr_t>(SetGlobalErrorHandler),
|
|
||||||
reinterpret_cast<intptr_t>(SetPromiseRejectHandler),
|
|
||||||
reinterpret_cast<intptr_t>(SetPromiseErrorExaminer),
|
|
||||||
0};
|
|
||||||
|
|
||||||
static const deno_buf empty_buf = {nullptr, 0, nullptr, 0};
|
static const deno_buf empty_buf = {nullptr, 0, nullptr, 0};
|
||||||
|
|
||||||
Deno* NewFromSnapshot(void* user_data, deno_recv_cb cb);
|
Deno* NewFromSnapshot(void* user_data, deno_recv_cb cb);
|
||||||
|
|
||||||
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
|
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
|
||||||
const char* js_filename, const char* js_source,
|
const char* js_filename, const char* js_source);
|
||||||
const char* source_map);
|
|
||||||
|
|
||||||
void HandleException(v8::Local<v8::Context> context,
|
void HandleException(v8::Local<v8::Context> context,
|
||||||
v8::Local<v8::Value> exception);
|
v8::Local<v8::Value> exception);
|
||||||
|
|
|
@ -15,14 +15,14 @@ TEST(LibDenoTest, InitializesCorrectlyWithoutSnapshot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LibDenoTest, SnapshotterInitializesCorrectly) {
|
TEST(LibDenoTest, SnapshotterInitializesCorrectly) {
|
||||||
Deno* d = deno_new_snapshotter(deno_config{empty, nullptr}, "a.js",
|
Deno* d =
|
||||||
"a = 1 + 2", nullptr);
|
deno_new_snapshotter(deno_config{empty, nullptr}, "a.js", "a = 1 + 2");
|
||||||
deno_delete(d);
|
deno_delete(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LibDenoTest, Snapshotter) {
|
TEST(LibDenoTest, Snapshotter) {
|
||||||
Deno* d1 = deno_new_snapshotter(deno_config{empty, nullptr}, "a.js",
|
Deno* d1 =
|
||||||
"a = 1 + 2", nullptr);
|
deno_new_snapshotter(deno_config{empty, nullptr}, "a.js", "a = 1 + 2");
|
||||||
deno_buf test_snapshot = deno_get_snapshot(d1);
|
deno_buf test_snapshot = deno_get_snapshot(d1);
|
||||||
deno_delete(d1);
|
deno_delete(d1);
|
||||||
|
|
||||||
|
@ -182,23 +182,18 @@ TEST(LibDenoTest, SnapshotBug) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LibDenoTest, GlobalErrorHandling) {
|
TEST(LibDenoTest, GlobalErrorHandling) {
|
||||||
static int count = 0;
|
|
||||||
auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) {
|
|
||||||
assert_null(data_buf);
|
|
||||||
count++;
|
|
||||||
EXPECT_EQ(static_cast<size_t>(1), buf.data_len);
|
|
||||||
EXPECT_EQ(buf.data_ptr[0], 42);
|
|
||||||
};
|
|
||||||
Deno* d = deno_new(snapshot, deno_config{empty, recv_cb});
|
|
||||||
EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "GlobalErrorHandling()"));
|
|
||||||
EXPECT_EQ(count, 1);
|
|
||||||
deno_delete(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(LibDenoTest, DoubleGlobalErrorHandlingFails) {
|
|
||||||
Deno* d = deno_new(snapshot, deno_config{empty, nullptr});
|
Deno* d = deno_new(snapshot, deno_config{empty, nullptr});
|
||||||
EXPECT_FALSE(
|
EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "GlobalErrorHandling()"));
|
||||||
deno_execute(d, nullptr, "a.js", "DoubleGlobalErrorHandlingFails()"));
|
// We only check that it starts with this string, so we don't have to check
|
||||||
|
// the second frame, which contains line numbers in libdeno_test.js and may
|
||||||
|
// change over time.
|
||||||
|
std::string expected =
|
||||||
|
"{\"message\":\"ReferenceError: notdefined is not defined\","
|
||||||
|
"\"frames\":[{\"line\":3,\"column\":2,\"functionName\":\"\","
|
||||||
|
"\"scriptName\":\"helloworld.js\",\"isEval\":true,"
|
||||||
|
"\"isConstructor\":false,\"isWasm\":false},";
|
||||||
|
std::string actual(deno_last_exception(d), 0, expected.length());
|
||||||
|
EXPECT_STREQ(expected.c_str(), actual.c_str());
|
||||||
deno_delete(d);
|
deno_delete(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,17 +220,31 @@ TEST(LibDenoTest, DataBuf) {
|
||||||
deno_delete(d);
|
deno_delete(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LibDenoTest, PromiseRejectCatchHandling) {
|
TEST(LibDenoTest, CheckPromiseErrors) {
|
||||||
static int count = 0;
|
static int count = 0;
|
||||||
auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) {
|
auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) { count++; };
|
||||||
// If no error, nothing should be sent, and count should
|
|
||||||
// not increment
|
|
||||||
count++;
|
|
||||||
};
|
|
||||||
Deno* d = deno_new(snapshot, deno_config{empty, recv_cb});
|
Deno* d = deno_new(snapshot, deno_config{empty, recv_cb});
|
||||||
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "PromiseRejectCatchHandling()"));
|
EXPECT_EQ(deno_last_exception(d), nullptr);
|
||||||
|
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "CheckPromiseErrors()"));
|
||||||
|
EXPECT_EQ(deno_last_exception(d), nullptr);
|
||||||
|
EXPECT_EQ(count, 1);
|
||||||
|
// We caught the exception. So still no errors after calling
|
||||||
|
// deno_check_promise_errors().
|
||||||
|
deno_check_promise_errors(d);
|
||||||
|
EXPECT_EQ(deno_last_exception(d), nullptr);
|
||||||
|
deno_delete(d);
|
||||||
|
}
|
||||||
|
|
||||||
EXPECT_EQ(count, 0);
|
TEST(LibDenoTest, LastException) {
|
||||||
|
Deno* d = deno_new(empty, deno_config{empty, nullptr});
|
||||||
|
EXPECT_EQ(deno_last_exception(d), nullptr);
|
||||||
|
EXPECT_FALSE(deno_execute(d, nullptr, "a.js", "\n\nthrow Error('boo');\n\n"));
|
||||||
|
EXPECT_STREQ(deno_last_exception(d),
|
||||||
|
"{\"message\":\"Error: boo\","
|
||||||
|
"\"frames\":[{\"line\":3,\"column\":7,"
|
||||||
|
"\"functionName\":\"\",\"scriptName\":\"a.js\","
|
||||||
|
"\"isEval\":false,"
|
||||||
|
"\"isConstructor\":false,\"isWasm\":false}]}");
|
||||||
deno_delete(d);
|
deno_delete(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,23 +99,9 @@ global.SnapshotBug = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
global.GlobalErrorHandling = () => {
|
global.GlobalErrorHandling = () => {
|
||||||
libdeno.setGlobalErrorHandler((message, source, line, col, error) => {
|
|
||||||
libdeno.print(`line ${line} col ${col}`, true);
|
|
||||||
assert("ReferenceError: notdefined is not defined" === message);
|
|
||||||
assert(source === "helloworld.js");
|
|
||||||
assert(line === 3);
|
|
||||||
assert(col === 1);
|
|
||||||
assert(error instanceof Error);
|
|
||||||
libdeno.send(new Uint8Array([42]));
|
|
||||||
});
|
|
||||||
eval("\n\n notdefined()\n//# sourceURL=helloworld.js");
|
eval("\n\n notdefined()\n//# sourceURL=helloworld.js");
|
||||||
};
|
};
|
||||||
|
|
||||||
global.DoubleGlobalErrorHandlingFails = () => {
|
|
||||||
libdeno.setGlobalErrorHandler((message, source, line, col, error) => {});
|
|
||||||
libdeno.setGlobalErrorHandler((message, source, line, col, error) => {});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allocate this buf at the top level to avoid GC.
|
// Allocate this buf at the top level to avoid GC.
|
||||||
const dataBuf = new Uint8Array([3, 4]);
|
const dataBuf = new Uint8Array([3, 4]);
|
||||||
|
|
||||||
|
@ -134,33 +120,7 @@ global.DataBuf = () => {
|
||||||
b[1] = 8;
|
b[1] = 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
global.PromiseRejectCatchHandling = () => {
|
global.CheckPromiseErrors = () => {
|
||||||
let count = 0;
|
|
||||||
let promiseRef = null;
|
|
||||||
// When we have an error, libdeno sends something
|
|
||||||
function assertOrSend(cond) {
|
|
||||||
if (!cond) {
|
|
||||||
libdeno.send(new Uint8Array([42]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
libdeno.setPromiseErrorExaminer(() => {
|
|
||||||
assertOrSend(count === 2);
|
|
||||||
});
|
|
||||||
libdeno.setPromiseRejectHandler((error, event, promise) => {
|
|
||||||
count++;
|
|
||||||
if (event === "RejectWithNoHandler") {
|
|
||||||
assertOrSend(error instanceof Error);
|
|
||||||
assertOrSend(error.message === "message");
|
|
||||||
assertOrSend(count === 1);
|
|
||||||
promiseRef = promise;
|
|
||||||
} else if (event === "HandlerAddedAfterReject") {
|
|
||||||
assertOrSend(count === 2);
|
|
||||||
assertOrSend(promiseRef === promise);
|
|
||||||
}
|
|
||||||
// Should never reach 3!
|
|
||||||
assertOrSend(count !== 3);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function fn() {
|
async function fn() {
|
||||||
throw new Error("message");
|
throw new Error("message");
|
||||||
}
|
}
|
||||||
|
@ -169,10 +129,10 @@ global.PromiseRejectCatchHandling = () => {
|
||||||
try {
|
try {
|
||||||
await fn();
|
await fn();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
assertOrSend(count === 2);
|
libdeno.send(new Uint8Array([42]));
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
};
|
||||||
|
|
||||||
global.Shared = () => {
|
global.Shared = () => {
|
||||||
const ab = libdeno.shared;
|
const ab = libdeno.shared;
|
||||||
|
@ -185,4 +145,4 @@ global.Shared = () => {
|
||||||
ui8[0] = 42;
|
ui8[0] = 42;
|
||||||
ui8[1] = 43;
|
ui8[1] = 43;
|
||||||
ui8[2] = 44;
|
ui8[2] = 44;
|
||||||
}
|
};
|
||||||
|
|
|
@ -30,9 +30,7 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
deno_init();
|
deno_init();
|
||||||
deno_config config = {deno::empty_buf, nullptr};
|
deno_config config = {deno::empty_buf, nullptr};
|
||||||
Deno* d = deno_new_snapshotter(
|
Deno* d = deno_new_snapshotter(config, js_fn, js_source.c_str());
|
||||||
config, js_fn, js_source.c_str(),
|
|
||||||
source_map_fn != nullptr ? source_map.c_str() : nullptr);
|
|
||||||
|
|
||||||
auto snapshot = deno_get_snapshot(d);
|
auto snapshot = deno_get_snapshot(d);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
"@types/base64-js": "^1.2.5",
|
"@types/base64-js": "^1.2.5",
|
||||||
"@types/flatbuffers": "^1.9.0",
|
"@types/flatbuffers": "^1.9.0",
|
||||||
"@types/prettier": "=1.15.3",
|
"@types/prettier": "=1.15.3",
|
||||||
"@types/source-map-support": "^0.4.1",
|
|
||||||
"@types/text-encoding": "0.0.33",
|
"@types/text-encoding": "0.0.33",
|
||||||
"base64-js": "^1.3.0",
|
"base64-js": "^1.3.0",
|
||||||
"flatbuffers": "^1.9.0",
|
"flatbuffers": "^1.9.0",
|
||||||
|
@ -19,7 +18,6 @@
|
||||||
"rollup-plugin-string": "^2.0.2",
|
"rollup-plugin-string": "^2.0.2",
|
||||||
"rollup-plugin-typescript2": "^0.16.1",
|
"rollup-plugin-typescript2": "^0.16.1",
|
||||||
"rollup-pluginutils": "^2.3.0",
|
"rollup-pluginutils": "^2.3.0",
|
||||||
"source-map-support": "^0.5.6",
|
|
||||||
"ts-node": "^7.0.1",
|
"ts-node": "^7.0.1",
|
||||||
"ts-simple-ast": "17.1.0",
|
"ts-simple-ast": "17.1.0",
|
||||||
"tslint": "^5.10.0",
|
"tslint": "^5.10.0",
|
||||||
|
|
|
@ -5,7 +5,9 @@ use errors::DenoResult;
|
||||||
use errors::ErrorKind;
|
use errors::ErrorKind;
|
||||||
use fs as deno_fs;
|
use fs as deno_fs;
|
||||||
use http_util;
|
use http_util;
|
||||||
|
use js_errors::SourceMapGetter;
|
||||||
use msg;
|
use msg;
|
||||||
|
|
||||||
use ring;
|
use ring;
|
||||||
use std;
|
use std;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
@ -346,6 +348,24 @@ impl DenoDir {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SourceMapGetter for DenoDir {
|
||||||
|
fn get_source_map(&self, script_name: &str) -> Option<String> {
|
||||||
|
match self.code_fetch(script_name, ".") {
|
||||||
|
Err(_e) => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Ok(out) => match out.maybe_source_map {
|
||||||
|
None => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(source_map) => {
|
||||||
|
return Some(source_map);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_cache_filename(basedir: &Path, url: &Url) -> PathBuf {
|
fn get_cache_filename(basedir: &Path, url: &Url) -> PathBuf {
|
||||||
let host = url.host_str().unwrap();
|
let host = url.host_str().unwrap();
|
||||||
let host_port = match url.port() {
|
let host_port = match url.port() {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use deno_dir;
|
||||||
use errors::DenoError;
|
use errors::DenoError;
|
||||||
use errors::DenoResult;
|
use errors::DenoResult;
|
||||||
use flags;
|
use flags;
|
||||||
|
use js_errors::JSError;
|
||||||
use libdeno;
|
use libdeno;
|
||||||
use permissions::DenoPermissions;
|
use permissions::DenoPermissions;
|
||||||
|
|
||||||
|
@ -26,8 +27,6 @@ use std::time::Instant;
|
||||||
use tokio;
|
use tokio;
|
||||||
use tokio_util;
|
use tokio_util;
|
||||||
|
|
||||||
type DenoException<'a> = &'a str;
|
|
||||||
|
|
||||||
// Buf represents a byte array returned from a "Op".
|
// Buf represents a byte array returned from a "Op".
|
||||||
// The message might be empty (which will be translated into a null object on
|
// The message might be empty (which will be translated into a null object on
|
||||||
// the javascript side) or it is a heap allocated opaque sequence of bytes.
|
// the javascript side) or it is a heap allocated opaque sequence of bytes.
|
||||||
|
@ -184,11 +183,25 @@ impl Isolate {
|
||||||
self.timeout_due.set(inst);
|
self.timeout_due.set(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn last_exception(&self) -> Option<JSError> {
|
||||||
|
let ptr = unsafe { libdeno::deno_last_exception(self.libdeno_isolate) };
|
||||||
|
if ptr == std::ptr::null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let cstr = unsafe { CStr::from_ptr(ptr) };
|
||||||
|
let v8_exception = cstr.to_str().unwrap();
|
||||||
|
debug!("v8_exception\n{}\n", v8_exception);
|
||||||
|
let js_error = JSError::from_v8_exception(v8_exception).unwrap();
|
||||||
|
let js_error_mapped = js_error.apply_source_map(&self.state.dir);
|
||||||
|
return Some(js_error_mapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
&self,
|
&self,
|
||||||
js_filename: &str,
|
js_filename: &str,
|
||||||
js_source: &str,
|
js_source: &str,
|
||||||
) -> Result<(), DenoException> {
|
) -> Result<(), JSError> {
|
||||||
let filename = CString::new(js_filename).unwrap();
|
let filename = CString::new(js_filename).unwrap();
|
||||||
let source = CString::new(js_source).unwrap();
|
let source = CString::new(js_source).unwrap();
|
||||||
let r = unsafe {
|
let r = unsafe {
|
||||||
|
@ -200,9 +213,8 @@ impl Isolate {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
let ptr = unsafe { libdeno::deno_last_exception(self.libdeno_isolate) };
|
let js_error = self.last_exception().unwrap();
|
||||||
let cstr = unsafe { CStr::from_ptr(ptr) };
|
return Err(js_error);
|
||||||
return Err(cstr.to_str().unwrap());
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -249,7 +261,7 @@ impl Isolate {
|
||||||
|
|
||||||
// TODO Use Park abstraction? Note at time of writing Tokio default runtime
|
// TODO Use Park abstraction? Note at time of writing Tokio default runtime
|
||||||
// does not have new_with_park().
|
// does not have new_with_park().
|
||||||
pub fn event_loop(&self) {
|
pub fn event_loop(&self) -> Result<(), JSError> {
|
||||||
// Main thread event loop.
|
// Main thread event loop.
|
||||||
while !self.is_idle() {
|
while !self.is_idle() {
|
||||||
match recv_deadline(&self.rx, self.get_timeout_due()) {
|
match recv_deadline(&self.rx, self.get_timeout_due()) {
|
||||||
|
@ -258,9 +270,16 @@ impl Isolate {
|
||||||
Err(e) => panic!("recv_deadline() failed: {:?}", e),
|
Err(e) => panic!("recv_deadline() failed: {:?}", e),
|
||||||
}
|
}
|
||||||
self.check_promise_errors();
|
self.check_promise_errors();
|
||||||
|
if let Some(err) = self.last_exception() {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Check on done
|
// Check on done
|
||||||
self.check_promise_errors();
|
self.check_promise_errors();
|
||||||
|
if let Some(err) = self.last_exception() {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -389,7 +408,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
).expect("execute error");
|
).expect("execute error");
|
||||||
isolate.event_loop();
|
isolate.event_loop().ok();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,8 +454,8 @@ mod tests {
|
||||||
const data = new Uint8Array([42, 43, 44, 45, 46]);
|
const data = new Uint8Array([42, 43, 44, 45, 46]);
|
||||||
libdeno.send(control, data);
|
libdeno.send(control, data);
|
||||||
"#,
|
"#,
|
||||||
).expect("execute error");
|
).expect("execute error");;
|
||||||
isolate.event_loop();
|
isolate.event_loop().unwrap();
|
||||||
let metrics = &isolate.state.metrics;
|
let metrics = &isolate.state.metrics;
|
||||||
assert_eq!(metrics.ops_dispatched.load(Ordering::SeqCst), 1);
|
assert_eq!(metrics.ops_dispatched.load(Ordering::SeqCst), 1);
|
||||||
assert_eq!(metrics.ops_completed.load(Ordering::SeqCst), 1);
|
assert_eq!(metrics.ops_completed.load(Ordering::SeqCst), 1);
|
||||||
|
@ -471,6 +490,7 @@ mod tests {
|
||||||
const control = new Uint8Array([4, 5, 6]);
|
const control = new Uint8Array([4, 5, 6]);
|
||||||
const data = new Uint8Array([42, 43, 44, 45, 46]);
|
const data = new Uint8Array([42, 43, 44, 45, 46]);
|
||||||
let r = libdeno.send(control, data);
|
let r = libdeno.send(control, data);
|
||||||
|
libdeno.recv(() => {});
|
||||||
if (r != null) throw Error("expected null");
|
if (r != null) throw Error("expected null");
|
||||||
"#,
|
"#,
|
||||||
).expect("execute error");
|
).expect("execute error");
|
||||||
|
@ -486,7 +506,7 @@ mod tests {
|
||||||
// with metrics_dispatch_async() to properly validate them.
|
// with metrics_dispatch_async() to properly validate them.
|
||||||
}
|
}
|
||||||
|
|
||||||
isolate.event_loop();
|
isolate.event_loop().unwrap();
|
||||||
|
|
||||||
// Make sure relevant metrics are updated after task is executed.
|
// Make sure relevant metrics are updated after task is executed.
|
||||||
{
|
{
|
||||||
|
|
543
src/js_errors.rs
Normal file
543
src/js_errors.rs
Normal file
|
@ -0,0 +1,543 @@
|
||||||
|
// Copyright 2018 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!");
|
||||||
|
// console.log(err.stack);
|
||||||
|
// It would require calling into Rust from Error.prototype.prepareStackTrace.
|
||||||
|
|
||||||
|
use serde_json;
|
||||||
|
use source_map_mappings::parse_mappings;
|
||||||
|
use source_map_mappings::Bias;
|
||||||
|
use source_map_mappings::Mappings;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub trait SourceMapGetter {
|
||||||
|
/// Returns the raw source map file.
|
||||||
|
fn get_source_map(&self, script_name: &str) -> Option<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SourceMap {
|
||||||
|
mappings: Mappings,
|
||||||
|
sources: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cached filename lookups. The key can be None if a previous lookup failed to
|
||||||
|
/// find a SourceMap.
|
||||||
|
type CachedMaps = HashMap<String, Option<SourceMap>>;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct StackFrame {
|
||||||
|
pub line: u32, // zero indexed
|
||||||
|
pub column: u32, // zero indexed
|
||||||
|
pub source_url: String, // TODO rename to 'script_name'
|
||||||
|
pub function_name: String,
|
||||||
|
pub is_eval: bool,
|
||||||
|
pub is_constructor: bool,
|
||||||
|
pub is_wasm: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct JSError {
|
||||||
|
pub message: String,
|
||||||
|
pub frames: Vec<StackFrame>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for StackFrame {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
// Note when we print to string, we change from 0-indexed to 1-indexed.
|
||||||
|
let (line, column) = (self.line + 1, self.column + 1);
|
||||||
|
if self.function_name.len() > 0 {
|
||||||
|
format!(
|
||||||
|
" at {} ({}:{}:{})",
|
||||||
|
self.function_name, self.source_url, line, column
|
||||||
|
)
|
||||||
|
} else if self.is_eval {
|
||||||
|
format!(" at eval ({}:{}:{})", self.source_url, line, column)
|
||||||
|
} else {
|
||||||
|
format!(" at {}:{}:{}", self.source_url, line, column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for JSError {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
let mut s = self.message.clone();
|
||||||
|
for frame in &self.frames {
|
||||||
|
s.push_str("\n");
|
||||||
|
s.push_str(&frame.to_string());
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackFrame {
|
||||||
|
// TODO Maybe use serde_derive?
|
||||||
|
fn from_json_value(v: &serde_json::Value) -> Option<Self> {
|
||||||
|
if !v.is_object() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let obj = v.as_object().unwrap();
|
||||||
|
|
||||||
|
let line_v = &obj["line"];
|
||||||
|
if !line_v.is_u64() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let line = line_v.as_u64().unwrap() as u32;
|
||||||
|
|
||||||
|
let column_v = &obj["column"];
|
||||||
|
if !column_v.is_u64() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let column = column_v.as_u64().unwrap() as u32;
|
||||||
|
|
||||||
|
let script_name_v = &obj["scriptName"];
|
||||||
|
if !script_name_v.is_string() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let script_name = String::from(script_name_v.as_str().unwrap());
|
||||||
|
|
||||||
|
// Optional fields. See EncodeExceptionAsJSON() in libdeno.
|
||||||
|
// Sometimes V8 doesn't provide all the frame information.
|
||||||
|
|
||||||
|
let mut function_name = String::from(""); // default
|
||||||
|
if obj.contains_key("functionName") {
|
||||||
|
let function_name_v = &obj["functionName"];
|
||||||
|
if function_name_v.is_string() {
|
||||||
|
function_name = String::from(function_name_v.as_str().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut is_eval = false; // default
|
||||||
|
if obj.contains_key("isEval") {
|
||||||
|
let is_eval_v = &obj["isEval"];
|
||||||
|
if is_eval_v.is_boolean() {
|
||||||
|
is_eval = is_eval_v.as_bool().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut is_constructor = false; // default
|
||||||
|
if obj.contains_key("isConstructor") {
|
||||||
|
let is_constructor_v = &obj["isConstructor"];
|
||||||
|
if is_constructor_v.is_boolean() {
|
||||||
|
is_constructor = is_constructor_v.as_bool().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut is_wasm = false; // default
|
||||||
|
if obj.contains_key("isWasm") {
|
||||||
|
let is_wasm_v = &obj["isWasm"];
|
||||||
|
if is_wasm_v.is_boolean() {
|
||||||
|
is_wasm = is_wasm_v.as_bool().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(StackFrame {
|
||||||
|
line: line - 1,
|
||||||
|
column: column - 1,
|
||||||
|
source_url: script_name,
|
||||||
|
function_name,
|
||||||
|
is_eval,
|
||||||
|
is_constructor,
|
||||||
|
is_wasm,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_source_map(
|
||||||
|
&self,
|
||||||
|
mappings_map: &mut CachedMaps,
|
||||||
|
getter: &SourceMapGetter,
|
||||||
|
) -> StackFrame {
|
||||||
|
let maybe_sm = get_mappings(self.source_url.as_ref(), mappings_map, getter);
|
||||||
|
let frame_pos = (self.source_url.to_owned(), self.line, self.column);
|
||||||
|
let (source_url, line, column) = match maybe_sm {
|
||||||
|
None => frame_pos,
|
||||||
|
Some(sm) => match sm.mappings.original_location_for(
|
||||||
|
self.line,
|
||||||
|
self.column,
|
||||||
|
Bias::default(),
|
||||||
|
) {
|
||||||
|
None => frame_pos,
|
||||||
|
Some(mapping) => match &mapping.original {
|
||||||
|
None => frame_pos,
|
||||||
|
Some(original) => {
|
||||||
|
let orig_source = sm.sources[original.source as usize].clone();
|
||||||
|
(
|
||||||
|
orig_source,
|
||||||
|
original.original_line,
|
||||||
|
original.original_column,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
StackFrame {
|
||||||
|
source_url,
|
||||||
|
function_name: self.function_name.clone(),
|
||||||
|
line,
|
||||||
|
column,
|
||||||
|
is_eval: self.is_eval,
|
||||||
|
is_constructor: self.is_constructor,
|
||||||
|
is_wasm: self.is_wasm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceMap {
|
||||||
|
fn from_json(json_str: &str) -> Option<Self> {
|
||||||
|
// Ugly. Maybe use serde_derive.
|
||||||
|
match serde_json::from_str::<serde_json::Value>(json_str) {
|
||||||
|
Ok(serde_json::Value::Object(map)) => match map["mappings"].as_str() {
|
||||||
|
None => return None,
|
||||||
|
Some(mappings_str) => {
|
||||||
|
match parse_mappings::<()>(mappings_str.as_bytes()) {
|
||||||
|
Err(_) => return None,
|
||||||
|
Ok(mappings) => {
|
||||||
|
if !map["sources"].is_array() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let sources_val = map["sources"].as_array().unwrap();
|
||||||
|
let mut sources = Vec::<String>::new();
|
||||||
|
|
||||||
|
for source_val in sources_val {
|
||||||
|
match source_val.as_str() {
|
||||||
|
None => return None,
|
||||||
|
Some(source) => {
|
||||||
|
sources.push(source.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(SourceMap { sources, mappings });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JSError {
|
||||||
|
/// Creates a new JSError by parsing the raw exception JSON string from V8.
|
||||||
|
pub fn from_v8_exception(json_str: &str) -> Option<Self> {
|
||||||
|
let v = serde_json::from_str::<serde_json::Value>(json_str);
|
||||||
|
if v.is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let v = v.unwrap();
|
||||||
|
|
||||||
|
if !v.is_object() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let obj = v.as_object().unwrap();
|
||||||
|
|
||||||
|
let message_v = &obj["message"];
|
||||||
|
if !message_v.is_string() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let message = String::from(message_v.as_str().unwrap());
|
||||||
|
|
||||||
|
let frames_v = &obj["frames"];
|
||||||
|
if !frames_v.is_array() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let frame_values = frames_v.as_array().unwrap();
|
||||||
|
|
||||||
|
let mut frames = Vec::<StackFrame>::new();
|
||||||
|
for frame_v in frame_values {
|
||||||
|
match StackFrame::from_json_value(frame_v) {
|
||||||
|
None => return None,
|
||||||
|
Some(frame) => frames.push(frame),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(JSError { message, frames })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_source_map(&self, getter: &SourceMapGetter) -> Self {
|
||||||
|
let message = self.message.clone();
|
||||||
|
let mut mappings_map: CachedMaps = HashMap::new();
|
||||||
|
let mut frames = Vec::<StackFrame>::new();
|
||||||
|
for frame in &self.frames {
|
||||||
|
let f = frame.apply_source_map(&mut mappings_map, getter);
|
||||||
|
frames.push(f);
|
||||||
|
}
|
||||||
|
JSError { message, frames }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_map_string(
|
||||||
|
source_url: &str,
|
||||||
|
getter: &SourceMapGetter,
|
||||||
|
) -> Option<SourceMap> {
|
||||||
|
match source_url {
|
||||||
|
"gen/bundle/main.js" => {
|
||||||
|
let s =
|
||||||
|
include_str!(concat!(env!("GN_OUT_DIR"), "/gen/bundle/main.js.map"));
|
||||||
|
SourceMap::from_json(s)
|
||||||
|
}
|
||||||
|
_ => match getter.get_source_map(source_url) {
|
||||||
|
None => None,
|
||||||
|
Some(raw_source_map) => SourceMap::from_json(&raw_source_map),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mappings<'a>(
|
||||||
|
source_url: &str,
|
||||||
|
mappings_map: &'a mut CachedMaps,
|
||||||
|
getter: &SourceMapGetter,
|
||||||
|
) -> &'a Option<SourceMap> {
|
||||||
|
mappings_map
|
||||||
|
.entry(source_url.to_string())
|
||||||
|
.or_insert_with(|| parse_map_string(source_url, getter))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn error1() -> JSError {
|
||||||
|
JSError {
|
||||||
|
message: "Error: foo bar".to_string(),
|
||||||
|
frames: vec![
|
||||||
|
StackFrame {
|
||||||
|
line: 4,
|
||||||
|
column: 16,
|
||||||
|
source_url: "foo_bar.ts".to_string(),
|
||||||
|
function_name: "foo".to_string(),
|
||||||
|
is_eval: false,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
},
|
||||||
|
StackFrame {
|
||||||
|
line: 5,
|
||||||
|
column: 20,
|
||||||
|
source_url: "bar_baz.ts".to_string(),
|
||||||
|
function_name: "qat".to_string(),
|
||||||
|
is_eval: false,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
},
|
||||||
|
StackFrame {
|
||||||
|
line: 1,
|
||||||
|
column: 1,
|
||||||
|
source_url: "deno_main.js".to_string(),
|
||||||
|
function_name: "".to_string(),
|
||||||
|
is_eval: false,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MockSourceMapGetter {}
|
||||||
|
|
||||||
|
impl SourceMapGetter for MockSourceMapGetter {
|
||||||
|
fn get_source_map(&self, script_name: &str) -> Option<String> {
|
||||||
|
let s = match script_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"}"#,
|
||||||
|
"bar_baz.ts" => r#"{"sources": ["bar_baz.ts"], "mappings":";;;IAEA,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,GAAG,GAAG,sDAAa,OAAO,2BAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,EAAE,CAAC;IAEQ,QAAA,GAAG,GAAG,KAAK,CAAC;IAEzB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}"#,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(s.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_frame_from_json_value_1() {
|
||||||
|
let v = serde_json::from_str::<serde_json::Value>(
|
||||||
|
r#"{
|
||||||
|
"line":2,
|
||||||
|
"column":11,
|
||||||
|
"functionName":"foo",
|
||||||
|
"scriptName":"/Users/rld/src/deno/tests/error_001.ts",
|
||||||
|
"isEval":true,
|
||||||
|
"isConstructor":false,
|
||||||
|
"isWasm":false
|
||||||
|
}"#,
|
||||||
|
).unwrap();
|
||||||
|
let r = StackFrame::from_json_value(&v);
|
||||||
|
assert_eq!(
|
||||||
|
r,
|
||||||
|
Some(StackFrame {
|
||||||
|
line: 1,
|
||||||
|
column: 10,
|
||||||
|
source_url: "/Users/rld/src/deno/tests/error_001.ts".to_string(),
|
||||||
|
function_name: "foo".to_string(),
|
||||||
|
is_eval: true,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_frame_from_json_value_2() {
|
||||||
|
let v = serde_json::from_str::<serde_json::Value>(
|
||||||
|
r#"{
|
||||||
|
"scriptName": "/Users/rld/src/deno/tests/error_001.ts",
|
||||||
|
"line": 2,
|
||||||
|
"column": 11
|
||||||
|
}"#,
|
||||||
|
).unwrap();
|
||||||
|
let r = StackFrame::from_json_value(&v);
|
||||||
|
assert!(r.is_some());
|
||||||
|
let f = r.unwrap();
|
||||||
|
assert_eq!(f.line, 1);
|
||||||
|
assert_eq!(f.column, 10);
|
||||||
|
assert_eq!(f.source_url, "/Users/rld/src/deno/tests/error_001.ts");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn js_error_from_v8_exception() {
|
||||||
|
let r = JSError::from_v8_exception(
|
||||||
|
r#"{
|
||||||
|
"message":"Uncaught Error: bad",
|
||||||
|
"frames":[
|
||||||
|
{
|
||||||
|
"line":2,
|
||||||
|
"column":11,
|
||||||
|
"functionName":"foo",
|
||||||
|
"scriptName":"/Users/rld/src/deno/tests/error_001.ts",
|
||||||
|
"isEval":true,
|
||||||
|
"isConstructor":false,
|
||||||
|
"isWasm":false
|
||||||
|
}, {
|
||||||
|
"line":5,
|
||||||
|
"column":5,
|
||||||
|
"functionName":"bar",
|
||||||
|
"scriptName":"/Users/rld/src/deno/tests/error_001.ts",
|
||||||
|
"isEval":true,
|
||||||
|
"isConstructor":false,
|
||||||
|
"isWasm":false
|
||||||
|
}
|
||||||
|
]}"#,
|
||||||
|
);
|
||||||
|
assert!(r.is_some());
|
||||||
|
let e = r.unwrap();
|
||||||
|
assert_eq!(e.message, "Uncaught Error: bad");
|
||||||
|
assert_eq!(e.frames.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
e.frames[0],
|
||||||
|
StackFrame {
|
||||||
|
line: 1,
|
||||||
|
column: 10,
|
||||||
|
source_url: "/Users/rld/src/deno/tests/error_001.ts".to_string(),
|
||||||
|
function_name: "foo".to_string(),
|
||||||
|
is_eval: true,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_frame_to_string() {
|
||||||
|
let e = error1();
|
||||||
|
assert_eq!(" at foo (foo_bar.ts:5:17)", e.frames[0].to_string());
|
||||||
|
assert_eq!(" at qat (bar_baz.ts:6:21)", e.frames[1].to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn js_error_to_string() {
|
||||||
|
let e = error1();
|
||||||
|
assert_eq!("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", e.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn js_error_apply_source_map_1() {
|
||||||
|
let e = error1();
|
||||||
|
let getter = MockSourceMapGetter {};
|
||||||
|
let actual = e.apply_source_map(&getter);
|
||||||
|
let expected = JSError {
|
||||||
|
message: "Error: foo bar".to_string(),
|
||||||
|
frames: vec![
|
||||||
|
StackFrame {
|
||||||
|
line: 5,
|
||||||
|
column: 12,
|
||||||
|
source_url: "foo_bar.ts".to_string(),
|
||||||
|
function_name: "foo".to_string(),
|
||||||
|
is_eval: false,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
},
|
||||||
|
StackFrame {
|
||||||
|
line: 4,
|
||||||
|
column: 14,
|
||||||
|
source_url: "bar_baz.ts".to_string(),
|
||||||
|
function_name: "qat".to_string(),
|
||||||
|
is_eval: false,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
},
|
||||||
|
StackFrame {
|
||||||
|
line: 1,
|
||||||
|
column: 1,
|
||||||
|
source_url: "deno_main.js".to_string(),
|
||||||
|
function_name: "".to_string(),
|
||||||
|
is_eval: false,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn js_error_apply_source_map_2() {
|
||||||
|
// Because this is accessing the live bundle, this test might be more fragile
|
||||||
|
let e = JSError {
|
||||||
|
message: "TypeError: baz".to_string(),
|
||||||
|
frames: vec![StackFrame {
|
||||||
|
line: 11,
|
||||||
|
column: 12,
|
||||||
|
source_url: "gen/bundle/main.js".to_string(),
|
||||||
|
function_name: "setLogDebug".to_string(),
|
||||||
|
is_eval: false,
|
||||||
|
is_constructor: false,
|
||||||
|
is_wasm: false,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
let getter = MockSourceMapGetter {};
|
||||||
|
let actual = e.apply_source_map(&getter);
|
||||||
|
assert_eq!(actual.message, "TypeError: baz");
|
||||||
|
assert_eq!(actual.frames.len(), 1);
|
||||||
|
assert_eq!(actual.frames[0].line, 15);
|
||||||
|
assert_eq!(actual.frames[0].column, 16);
|
||||||
|
assert_eq!(actual.frames[0].source_url, "deno/js/util.ts");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn source_map_from_json() {
|
||||||
|
let json = r#"{"version":3,"file":"error_001.js","sourceRoot":"","sources":["file:///Users/rld/src/deno/tests/error_001.ts"],"names":[],"mappings":"AAAA,SAAS,GAAG;IACV,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,GAAG;IACV,GAAG,EAAE,CAAC;AACR,CAAC;AAED,GAAG,EAAE,CAAC"}"#;
|
||||||
|
let sm = SourceMap::from_json(json).unwrap();
|
||||||
|
assert_eq!(sm.sources.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
sm.sources[0],
|
||||||
|
"file:///Users/rld/src/deno/tests/error_001.ts"
|
||||||
|
);
|
||||||
|
let mapping = sm
|
||||||
|
.mappings
|
||||||
|
.original_location_for(1, 10, Bias::default())
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(mapping.generated_line, 1);
|
||||||
|
assert_eq!(mapping.generated_column, 10);
|
||||||
|
assert_eq!(
|
||||||
|
mapping.original,
|
||||||
|
Some(source_map_mappings::OriginalLocation {
|
||||||
|
source: 0,
|
||||||
|
original_line: 1,
|
||||||
|
original_column: 8,
|
||||||
|
name: None
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
17
src/main.rs
17
src/main.rs
|
@ -10,6 +10,8 @@ extern crate rand;
|
||||||
extern crate remove_dir_all;
|
extern crate remove_dir_all;
|
||||||
extern crate ring;
|
extern crate ring;
|
||||||
extern crate rustyline;
|
extern crate rustyline;
|
||||||
|
extern crate serde_json;
|
||||||
|
extern crate source_map_mappings;
|
||||||
extern crate tempfile;
|
extern crate tempfile;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
extern crate tokio_executor;
|
extern crate tokio_executor;
|
||||||
|
@ -33,6 +35,7 @@ mod fs;
|
||||||
mod http_body;
|
mod http_body;
|
||||||
mod http_util;
|
mod http_util;
|
||||||
pub mod isolate;
|
pub mod isolate;
|
||||||
|
pub mod js_errors;
|
||||||
pub mod libdeno;
|
pub mod libdeno;
|
||||||
pub mod msg;
|
pub mod msg;
|
||||||
pub mod msg_util;
|
pub mod msg_util;
|
||||||
|
@ -68,6 +71,13 @@ impl log::Log for Logger {
|
||||||
fn flush(&self) {}
|
fn flush(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_err_and_exit(err: js_errors::JSError) {
|
||||||
|
// TODO Currently tests depend on exception going to stdout. It should go
|
||||||
|
// to stderr. https://github.com/denoland/deno/issues/964
|
||||||
|
println!("{}", err.to_string());
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Rust does not die on panic by default. And -Cpanic=abort is broken.
|
// Rust does not die on panic by default. And -Cpanic=abort is broken.
|
||||||
// https://github.com/rust-lang/cargo/issues/2738
|
// https://github.com/rust-lang/cargo/issues/2738
|
||||||
|
@ -102,10 +112,7 @@ fn main() {
|
||||||
tokio_util::init(|| {
|
tokio_util::init(|| {
|
||||||
isolate
|
isolate
|
||||||
.execute("deno_main.js", "denoMain();")
|
.execute("deno_main.js", "denoMain();")
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(print_err_and_exit);
|
||||||
error!("{}", err);
|
isolate.event_loop().unwrap_or_else(print_err_and_exit);
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
isolate.event_loop();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,7 @@ world
|
||||||
Error: error
|
Error: error
|
||||||
at foo ([WILDCARD]tests/async_error.ts:4:9)
|
at foo ([WILDCARD]tests/async_error.ts:4:9)
|
||||||
at eval ([WILDCARD]tests/async_error.ts:7:1)
|
at eval ([WILDCARD]tests/async_error.ts:7:1)
|
||||||
at Runner.eval [as _globalEval] (<anonymous>)
|
at _gatherDependencies ([WILDCARD]/js/runner.ts:[WILDCARD])
|
||||||
at Runner._gatherDependencies ([WILDCARD]/js/runner.ts:[WILDCARD])
|
at run ([WILDCARD]/js/runner.ts:[WILDCARD])
|
||||||
at Runner.run ([WILDCARD]/js/runner.ts:[WILDCARD])
|
|
||||||
at denoMain ([WILDCARD]/js/main.ts:[WILDCARD])
|
at denoMain ([WILDCARD]/js/main.ts:[WILDCARD])
|
||||||
at deno_main.js:1:1
|
at deno_main.js:1:1
|
||||||
|
|
|
@ -2,8 +2,7 @@ Error: bad
|
||||||
at foo (file://[WILDCARD]tests/error_001.ts:2:9)
|
at foo (file://[WILDCARD]tests/error_001.ts:2:9)
|
||||||
at bar (file://[WILDCARD]tests/error_001.ts:6:3)
|
at bar (file://[WILDCARD]tests/error_001.ts:6:3)
|
||||||
at eval (file://[WILDCARD]tests/error_001.ts:9:1)
|
at eval (file://[WILDCARD]tests/error_001.ts:9:1)
|
||||||
at Runner.eval [as _globalEval] (<anonymous>)
|
at _gatherDependencies ([WILDCARD]/js/runner.ts:[WILDCARD])
|
||||||
at Runner._gatherDependencies ([WILDCARD]/js/runner.ts:[WILDCARD])
|
at run ([WILDCARD]/js/runner.ts:[WILDCARD])
|
||||||
at Runner.run ([WILDCARD]/js/runner.ts:[WILDCARD])
|
|
||||||
at denoMain ([WILDCARD]/js/main.ts:[WILDCARD])
|
at denoMain ([WILDCARD]/js/main.ts:[WILDCARD])
|
||||||
at deno_main.js:1:1
|
at deno_main.js:1:1
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
Error: exception from mod1
|
Error: exception from mod1
|
||||||
at Object.throwsError (file://[WILDCARD]/tests/subdir/mod1.ts:16:9)
|
at throwsError (file://[WILDCARD]/tests/subdir/mod1.ts:16:9)
|
||||||
at foo (file://[WILDCARD]/tests/error_002.ts:4:3)
|
at foo (file://[WILDCARD]/tests/error_002.ts:4:3)
|
||||||
at Module.eval [as factory ] (file://[WILDCARD]/tests/error_002.ts:7:1)
|
at eval (file://[WILDCARD]/tests/error_002.ts:7:1)
|
||||||
at Runner._drainRunQueue ([WILDCARD]/js/runner.ts:[WILDCARD])
|
at _drainRunQueue ([WILDCARD]/js/runner.ts:[WILDCARD])
|
||||||
at Runner.run ([WILDCARD]/js/runner.ts:[WILDCARD])
|
at run ([WILDCARD]/js/runner.ts:[WILDCARD])
|
||||||
at denoMain ([WILDCARD]/js/main.ts:[WILDCARD])
|
at denoMain ([WILDCARD]/js/main.ts:[WILDCARD])
|
||||||
at deno_main.js:1:1
|
at deno_main.js:1:1
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
NotFound: Cannot resolve module "bad-module.ts" from "[WILDCARD]/tests/error_004_missing_module.ts"
|
NotFound: Cannot resolve module "bad-module.ts" from "[WILDCARD]/tests/error_004_missing_module.ts"
|
||||||
|
at DenoError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at maybeError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
at maybeError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at maybeThrowError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
at maybeThrowError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at sendSync ([WILDCARD]/js/dispatch.ts:[WILDCARD])
|
at sendSync ([WILDCARD]/js/dispatch.ts:[WILDCARD])
|
||||||
at Object.codeFetch ([WILDCARD]/js/os.ts:[WILDCARD])
|
at codeFetch ([WILDCARD]/js/os.ts:[WILDCARD])
|
||||||
at Compiler._resolveModule ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at _resolveModule ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at moduleNames.map.name ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at moduleNames.map.name ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at Array.map (<anonymous>)
|
at resolveModuleNames ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at Compiler.resolveModuleNames ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at compilerHost.resolveModuleNames ([WILDCARD]typescript.js:[WILDCARD])
|
||||||
at Object.compilerHost.resolveModuleNames (<anonymous>)
|
at resolveModuleNamesWorker ([WILDCARD]typescript.js:[WILDCARD])
|
||||||
at resolveModuleNamesWorker (<anonymous>)
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
NotFound: Cannot resolve module "bad-module.ts" from "[WILDCARD]/tests/error_005_missing_dynamic_import.ts"
|
NotFound: Cannot resolve module "bad-module.ts" from "[WILDCARD]/tests/error_005_missing_dynamic_import.ts"
|
||||||
|
at DenoError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at maybeError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
at maybeError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at maybeThrowError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
at maybeThrowError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at sendSync ([WILDCARD]/js/dispatch.ts:[WILDCARD])
|
at sendSync ([WILDCARD]/js/dispatch.ts:[WILDCARD])
|
||||||
at Object.codeFetch ([WILDCARD]/js/os.ts:[WILDCARD])
|
at codeFetch ([WILDCARD]/js/os.ts:[WILDCARD])
|
||||||
at Compiler._resolveModule ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at _resolveModule ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at moduleNames.map.name ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at moduleNames.map.name ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at Array.map (<anonymous>)
|
at resolveModuleNames ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at Compiler.resolveModuleNames ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at compilerHost.resolveModuleNames ([WILDCARD])
|
||||||
at Object.compilerHost.resolveModuleNames (<anonymous>)
|
at resolveModuleNamesWorker ([WILDCARD])
|
||||||
at resolveModuleNamesWorker (<anonymous>)
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
NotFound: Cannot resolve module "./non-existent" from "[WILDCARD]/tests/error_006_import_ext_failure.ts"
|
NotFound: Cannot resolve module "./non-existent" from "[WILDCARD]/tests/error_006_import_ext_failure.ts"
|
||||||
|
at DenoError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at maybeError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
at maybeError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at maybeThrowError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
at maybeThrowError ([WILDCARD]/js/errors.ts:[WILDCARD])
|
||||||
at sendSync ([WILDCARD]/js/dispatch.ts:[WILDCARD])
|
at sendSync ([WILDCARD]/js/dispatch.ts:[WILDCARD])
|
||||||
at Object.codeFetch ([WILDCARD]/js/os.ts:[WILDCARD])
|
at codeFetch ([WILDCARD]/js/os.ts:[WILDCARD])
|
||||||
at Compiler._resolveModule ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at _resolveModule ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at moduleNames.map.name ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at moduleNames.map.name ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at Array.map (<anonymous>)
|
at resolveModuleNames ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
||||||
at Compiler.resolveModuleNames ([WILDCARD]/js/compiler.ts:[WILDCARD])
|
at compilerHost.resolveModuleNames ([WILDCARD])
|
||||||
at Object.compilerHost.resolveModuleNames (<anonymous>)
|
at resolveModuleNamesWorker ([WILDCARD])
|
||||||
at resolveModuleNamesWorker (<anonymous>)
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Thrown: [object Object]
|
[object Object]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit e058979631fd3ecc55f8995a02eaa6ff8f35c321
|
Subproject commit 7d8c9aa769778140e1619f545e706bf34545509e
|
Loading…
Add table
Reference in a new issue