summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/rt/40_error_stack.js226
-rw-r--r--cli/rt/99_main.js9
-rw-r--r--cli/tests/unit/error_stack_test.ts99
-rw-r--r--core/error.js247
-rw-r--r--core/runtime.rs3
5 files changed, 258 insertions, 326 deletions
diff --git a/cli/rt/40_error_stack.js b/cli/rt/40_error_stack.js
index 834503e34..da2ee51f3 100644
--- a/cli/rt/40_error_stack.js
+++ b/cli/rt/40_error_stack.js
@@ -1,11 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
((window) => {
- // Some of the code here is adapted directly from V8 and licensed under a BSD
- // style license available here: https://github.com/v8/v8/blob/24886f2d1c565287d33d71e4109a53bf0b54b75c/LICENSE.v8
const core = window.Deno.core;
- const assert = window.__bootstrap.util.assert;
- const internals = window.__bootstrap.internals;
function opFormatDiagnostics(diagnostics) {
return core.jsonOpSync("op_format_diagnostic", diagnostics);
@@ -20,229 +16,7 @@
};
}
- function patchCallSite(callSite, location) {
- return {
- getThis() {
- return callSite.getThis();
- },
- getTypeName() {
- return callSite.getTypeName();
- },
- getFunction() {
- return callSite.getFunction();
- },
- getFunctionName() {
- return callSite.getFunctionName();
- },
- getMethodName() {
- return callSite.getMethodName();
- },
- getFileName() {
- return location.fileName;
- },
- getLineNumber() {
- return location.lineNumber;
- },
- getColumnNumber() {
- return location.columnNumber;
- },
- getEvalOrigin() {
- return callSite.getEvalOrigin();
- },
- isToplevel() {
- return callSite.isToplevel();
- },
- isEval() {
- return callSite.isEval();
- },
- isNative() {
- return callSite.isNative();
- },
- isConstructor() {
- return callSite.isConstructor();
- },
- isAsync() {
- return callSite.isAsync();
- },
- isPromiseAll() {
- return callSite.isPromiseAll();
- },
- getPromiseIndex() {
- return callSite.getPromiseIndex();
- },
- };
- }
-
- // Keep in sync with `cli/fmt_errors.rs`.
- function formatLocation(callSite) {
- if (callSite.isNative()) {
- return "native";
- }
-
- let result = "";
-
- const fileName = callSite.getFileName();
-
- if (fileName) {
- result += fileName;
- } else {
- if (callSite.isEval()) {
- const evalOrigin = callSite.getEvalOrigin();
- assert(evalOrigin != null);
- result += `${evalOrigin}, `;
- }
- result += "<anonymous>";
- }
-
- const lineNumber = callSite.getLineNumber();
- if (lineNumber != null) {
- result += `:${lineNumber}`;
-
- const columnNumber = callSite.getColumnNumber();
- if (columnNumber != null) {
- result += `:${columnNumber}`;
- }
- }
-
- return result;
- }
-
- // Keep in sync with `cli/fmt_errors.rs`.
- function formatCallSite(callSite) {
- let result = "";
- const functionName = callSite.getFunctionName();
-
- const isTopLevel = callSite.isToplevel();
- const isAsync = callSite.isAsync();
- const isPromiseAll = callSite.isPromiseAll();
- const isConstructor = callSite.isConstructor();
- const isMethodCall = !(isTopLevel || isConstructor);
-
- if (isAsync) {
- result += "async ";
- }
- if (isPromiseAll) {
- result += `Promise.all (index ${callSite.getPromiseIndex()})`;
- return result;
- }
- if (isMethodCall) {
- const typeName = callSite.getTypeName();
- const methodName = callSite.getMethodName();
-
- if (functionName) {
- if (typeName) {
- if (!functionName.startsWith(typeName)) {
- result += `${typeName}.`;
- }
- }
- result += functionName;
- if (methodName) {
- if (!functionName.endsWith(methodName)) {
- result += ` [as ${methodName}]`;
- }
- }
- } else {
- if (typeName) {
- result += `${typeName}.`;
- }
- if (methodName) {
- result += methodName;
- } else {
- result += "<anonymous>";
- }
- }
- } else if (isConstructor) {
- result += "new ";
- if (functionName) {
- result += functionName;
- } else {
- result += "<anonymous>";
- }
- } else if (functionName) {
- result += functionName;
- } else {
- result += formatLocation(callSite);
- return result;
- }
-
- result += ` (${formatLocation(callSite)})`;
- return result;
- }
-
- function evaluateCallSite(callSite) {
- return {
- this: callSite.getThis(),
- typeName: callSite.getTypeName(),
- function: callSite.getFunction(),
- functionName: callSite.getFunctionName(),
- methodName: callSite.getMethodName(),
- fileName: callSite.getFileName(),
- lineNumber: callSite.getLineNumber(),
- columnNumber: callSite.getColumnNumber(),
- evalOrigin: callSite.getEvalOrigin(),
- isToplevel: callSite.isToplevel(),
- isEval: callSite.isEval(),
- isNative: callSite.isNative(),
- isConstructor: callSite.isConstructor(),
- isAsync: callSite.isAsync(),
- isPromiseAll: callSite.isPromiseAll(),
- promiseIndex: callSite.getPromiseIndex(),
- };
- }
-
- function prepareStackTrace(
- error,
- callSites,
- ) {
- const mappedCallSites = callSites.map(
- (callSite) => {
- const fileName = callSite.getFileName();
- const lineNumber = callSite.getLineNumber();
- const columnNumber = callSite.getColumnNumber();
- if (fileName && lineNumber != null && columnNumber != null) {
- return patchCallSite(
- callSite,
- opApplySourceMap({
- fileName,
- lineNumber,
- columnNumber,
- }),
- );
- }
- return callSite;
- },
- );
- Object.defineProperties(error, {
- __callSiteEvals: { value: [], configurable: true },
- });
- const formattedCallSites = [];
- for (const callSite of mappedCallSites) {
- error.__callSiteEvals.push(Object.freeze(evaluateCallSite(callSite)));
- formattedCallSites.push(formatCallSite(callSite));
- }
- Object.freeze(error.__callSiteEvals);
- const message = error.message !== undefined ? error.message : "";
- const name = error.name !== undefined ? error.name : "Error";
- let messageLine;
- if (name != "" && message != "") {
- messageLine = `${name}: ${message}`;
- } else if ((name || message) != "") {
- messageLine = name || message;
- } else {
- messageLine = "";
- }
- return messageLine +
- formattedCallSites.map((s) => `\n at ${s}`).join("");
- }
-
- function setPrepareStackTrace(ErrorConstructor) {
- ErrorConstructor.prepareStackTrace = prepareStackTrace;
- }
-
- internals.exposeForTest("setPrepareStackTrace", setPrepareStackTrace);
-
window.__bootstrap.errorStack = {
- setPrepareStackTrace,
opApplySourceMap,
opFormatDiagnostics,
};
diff --git a/cli/rt/99_main.js b/cli/rt/99_main.js
index 40c9c636f..2aa140990 100644
--- a/cli/rt/99_main.js
+++ b/cli/rt/99_main.js
@@ -164,9 +164,16 @@ delete Object.prototype.__proto__;
// TODO(bartlomieju): a very crude way to disable
// source mapping of errors. This condition is true
// only for compiled standalone binaries.
+ let prepareStackTrace;
if (s.applySourceMaps) {
- errorStack.setPrepareStackTrace(Error);
+ prepareStackTrace = core.createPrepareStackTrace(
+ errorStack.opApplySourceMap,
+ );
+ } else {
+ prepareStackTrace = core.createPrepareStackTrace();
}
+ Error.prepareStackTrace = prepareStackTrace;
+
return s;
}
diff --git a/cli/tests/unit/error_stack_test.ts b/cli/tests/unit/error_stack_test.ts
index a5fe13796..ad5f2e093 100644
--- a/cli/tests/unit/error_stack_test.ts
+++ b/cli/tests/unit/error_stack_test.ts
@@ -1,87 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals, assertMatch, unitTest } from "./test_util.ts";
-// @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol
-const { setPrepareStackTrace } = Deno[Deno.internal];
-
-interface CallSite {
- getThis(): unknown;
- getTypeName(): string | null;
- // deno-lint-ignore ban-types
- getFunction(): Function | null;
- getFunctionName(): string | null;
- getMethodName(): string | null;
- getFileName(): string | null;
- getLineNumber(): number | null;
- getColumnNumber(): number | null;
- getEvalOrigin(): string | null;
- isToplevel(): boolean | null;
- isEval(): boolean;
- isNative(): boolean;
- isConstructor(): boolean;
- isAsync(): boolean;
- isPromiseAll(): boolean;
- getPromiseIndex(): number | null;
-}
-
-function getMockCallSite(
- fileName: string,
- lineNumber: number | null,
- columnNumber: number | null,
-): CallSite {
- return {
- getThis(): unknown {
- return undefined;
- },
- getTypeName(): string {
- return "";
- },
- // deno-lint-ignore ban-types
- getFunction(): Function {
- return (): void => {};
- },
- getFunctionName(): string {
- return "";
- },
- getMethodName(): string {
- return "";
- },
- getFileName(): string {
- return fileName;
- },
- getLineNumber(): number | null {
- return lineNumber;
- },
- getColumnNumber(): number | null {
- return columnNumber;
- },
- getEvalOrigin(): null {
- return null;
- },
- isToplevel(): false {
- return false;
- },
- isEval(): false {
- return false;
- },
- isNative(): false {
- return false;
- },
- isConstructor(): false {
- return false;
- },
- isAsync(): false {
- return false;
- },
- isPromiseAll(): false {
- return false;
- },
- getPromiseIndex(): null {
- return null;
- },
- };
-}
-
unitTest(function errorStackMessageLine(): void {
const e1 = new Error();
e1.name = "Foo";
@@ -122,24 +41,6 @@ unitTest(function errorStackMessageLine(): void {
assertMatch(e6.stack!, /^null: null\n/);
});
-// FIXME(bartlomieju): no longer works after migrating
-// to JavaScript runtime code
-unitTest({ ignore: true }, function prepareStackTrace(): void {
- // deno-lint-ignore no-explicit-any
- const MockError = {} as any;
- setPrepareStackTrace(MockError);
- assert(typeof MockError.prepareStackTrace === "function");
- const prepareStackTrace: (
- error: Error,
- structuredStackTrace: CallSite[],
- ) => string = MockError.prepareStackTrace;
- const result = prepareStackTrace(new Error("foo"), [
- getMockCallSite("CLI_SNAPSHOT.js", 23, 0),
- ]);
- assert(result.startsWith("Error: foo\n"));
- assert(result.includes(".ts:"), "should remap to something in 'js/'");
-});
-
unitTest(function captureStackTrace(): void {
function foo(): void {
const error = new Error();
diff --git a/core/error.js b/core/error.js
new file mode 100644
index 000000000..708b1fd4a
--- /dev/null
+++ b/core/error.js
@@ -0,0 +1,247 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+((window) => {
+ // Some of the code here is adapted directly from V8 and licensed under a BSD
+ // style license available here: https://github.com/v8/v8/blob/24886f2d1c565287d33d71e4109a53bf0b54b75c/LICENSE.v8
+ function patchCallSite(callSite, location) {
+ return {
+ getThis() {
+ return callSite.getThis();
+ },
+ getTypeName() {
+ return callSite.getTypeName();
+ },
+ getFunction() {
+ return callSite.getFunction();
+ },
+ getFunctionName() {
+ return callSite.getFunctionName();
+ },
+ getMethodName() {
+ return callSite.getMethodName();
+ },
+ getFileName() {
+ return location.fileName;
+ },
+ getLineNumber() {
+ return location.lineNumber;
+ },
+ getColumnNumber() {
+ return location.columnNumber;
+ },
+ getEvalOrigin() {
+ return callSite.getEvalOrigin();
+ },
+ isToplevel() {
+ return callSite.isToplevel();
+ },
+ isEval() {
+ return callSite.isEval();
+ },
+ isNative() {
+ return callSite.isNative();
+ },
+ isConstructor() {
+ return callSite.isConstructor();
+ },
+ isAsync() {
+ return callSite.isAsync();
+ },
+ isPromiseAll() {
+ return callSite.isPromiseAll();
+ },
+ getPromiseIndex() {
+ return callSite.getPromiseIndex();
+ },
+ };
+ }
+
+ // Keep in sync with `cli/fmt_errors.rs`.
+ function formatLocation(callSite) {
+ if (callSite.isNative()) {
+ return "native";
+ }
+
+ let result = "";
+
+ const fileName = callSite.getFileName();
+
+ if (fileName) {
+ result += fileName;
+ } else {
+ if (callSite.isEval()) {
+ const evalOrigin = callSite.getEvalOrigin();
+ if (evalOrigin == null) {
+ throw new Error("assert evalOrigin");
+ }
+ result += `${evalOrigin}, `;
+ }
+ result += "<anonymous>";
+ }
+
+ const lineNumber = callSite.getLineNumber();
+ if (lineNumber != null) {
+ result += `:${lineNumber}`;
+
+ const columnNumber = callSite.getColumnNumber();
+ if (columnNumber != null) {
+ result += `:${columnNumber}`;
+ }
+ }
+
+ return result;
+ }
+
+ // Keep in sync with `cli/fmt_errors.rs`.
+ function formatCallSite(callSite) {
+ let result = "";
+ const functionName = callSite.getFunctionName();
+
+ const isTopLevel = callSite.isToplevel();
+ const isAsync = callSite.isAsync();
+ const isPromiseAll = callSite.isPromiseAll();
+ const isConstructor = callSite.isConstructor();
+ const isMethodCall = !(isTopLevel || isConstructor);
+
+ if (isAsync) {
+ result += "async ";
+ }
+ if (isPromiseAll) {
+ result += `Promise.all (index ${callSite.getPromiseIndex()})`;
+ return result;
+ }
+ if (isMethodCall) {
+ const typeName = callSite.getTypeName();
+ const methodName = callSite.getMethodName();
+
+ if (functionName) {
+ if (typeName) {
+ if (!functionName.startsWith(typeName)) {
+ result += `${typeName}.`;
+ }
+ }
+ result += functionName;
+ if (methodName) {
+ if (!functionName.endsWith(methodName)) {
+ result += ` [as ${methodName}]`;
+ }
+ }
+ } else {
+ if (typeName) {
+ result += `${typeName}.`;
+ }
+ if (methodName) {
+ result += methodName;
+ } else {
+ result += "<anonymous>";
+ }
+ }
+ } else if (isConstructor) {
+ result += "new ";
+ if (functionName) {
+ result += functionName;
+ } else {
+ result += "<anonymous>";
+ }
+ } else if (functionName) {
+ result += functionName;
+ } else {
+ result += formatLocation(callSite);
+ return result;
+ }
+
+ result += ` (${formatLocation(callSite)})`;
+ return result;
+ }
+
+ function evaluateCallSite(callSite) {
+ return {
+ this: callSite.getThis(),
+ typeName: callSite.getTypeName(),
+ function: callSite.getFunction(),
+ functionName: callSite.getFunctionName(),
+ methodName: callSite.getMethodName(),
+ fileName: callSite.getFileName(),
+ lineNumber: callSite.getLineNumber(),
+ columnNumber: callSite.getColumnNumber(),
+ evalOrigin: callSite.getEvalOrigin(),
+ isToplevel: callSite.isToplevel(),
+ isEval: callSite.isEval(),
+ isNative: callSite.isNative(),
+ isConstructor: callSite.isConstructor(),
+ isAsync: callSite.isAsync(),
+ isPromiseAll: callSite.isPromiseAll(),
+ promiseIndex: callSite.getPromiseIndex(),
+ };
+ }
+
+ /**
+ * Returns a function that can be used as `Error.prepareStackTrace`.
+ *
+ * This function accepts an optional argument, a function that performs
+ * source mapping. It is not required to pass this argument, but
+ * in such case only JavaScript sources will have proper position in
+ * stack frames.
+ * @param {(
+ * fileName: string,
+ * lineNumber: number,
+ * columnNumber: number
+ * ) => {
+ * fileName: string,
+ * lineNumber: number,
+ * columnNumber: number
+ * }} sourceMappingFn
+ */
+ function createPrepareStackTrace(sourceMappingFn) {
+ return function prepareStackTrace(
+ error,
+ callSites,
+ ) {
+ const mappedCallSites = callSites.map(
+ (callSite) => {
+ const fileName = callSite.getFileName();
+ const lineNumber = callSite.getLineNumber();
+ const columnNumber = callSite.getColumnNumber();
+ if (
+ sourceMappingFn && fileName && lineNumber != null &&
+ columnNumber != null
+ ) {
+ return patchCallSite(
+ callSite,
+ sourceMappingFn({
+ fileName,
+ lineNumber,
+ columnNumber,
+ }),
+ );
+ }
+ return callSite;
+ },
+ );
+ Object.defineProperties(error, {
+ __callSiteEvals: { value: [], configurable: true },
+ });
+ const formattedCallSites = [];
+ for (const callSite of mappedCallSites) {
+ error.__callSiteEvals.push(evaluateCallSite(callSite));
+ formattedCallSites.push(formatCallSite(callSite));
+ }
+ const message = error.message !== undefined ? error.message : "";
+ const name = error.name !== undefined ? error.name : "Error";
+ let messageLine;
+ if (name != "" && message != "") {
+ messageLine = `${name}: ${message}`;
+ } else if ((name || message) != "") {
+ messageLine = name || message;
+ } else {
+ messageLine = "";
+ }
+ return messageLine +
+ formattedCallSites.map((s) => `\n at ${s}`).join("");
+ };
+ }
+
+ Object.assign(window.Deno.core, {
+ createPrepareStackTrace,
+ });
+})(this);
diff --git a/core/runtime.rs b/core/runtime.rs
index ecac588ca..0f09926f8 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -329,6 +329,9 @@ impl JsRuntime {
self
.execute("deno:core/core.js", include_str!("core.js"))
.unwrap();
+ self
+ .execute("deno:core/error.js", include_str!("error.js"))
+ .unwrap();
}
}