Skip to content

Commit

Permalink
Better error stack traces
Browse files Browse the repository at this point in the history
  • Loading branch information
hoodmane committed May 16, 2023
1 parent cd44c9b commit cc08aab
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 16 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"devDependencies": {
"chai": "4.2.0",
"conditional-type-checks": "1.0.5",
"error-stack-parser": "^2.1.4",
"esbuild": "^0.17.15",
"husky": "4.2.5",
"karma": "^6.4.1",
Expand Down
27 changes: 16 additions & 11 deletions src/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,46 +69,51 @@ export class SynclinkTask<T> {
});
}

schedule_async(): this {
_schedule_async(): Promise<T> {
if (this.mode === "async") {
// already scheduled
return this;
return this._promise;
}
if (this.mode === "sync") {
throw new Error("Already synchronously scheduled");
}
this.mode = "async";
this.do_async().then(
const p = this.do_async().then(
(value) => {
// console.log("resolving", this.taskId, "value", value);
this._resolved = true;
this._result = value;
this._resolve(value);
return value;
},
(reason) => {
this._exception = reason;
this._reject(reason);
throw reason;
},
);
this._promise = p;
return p;
}

schedule_async(): this {
this._schedule_async();
return this;
}

async then<S>(
then<S>(
onfulfilled: (value: T) => S,
onrejected: (reason: any) => S,
): Promise<S> {
this.schedule_async();
return this._promise.then(onfulfilled, onrejected);
return this._schedule_async().then(onfulfilled, onrejected);
}

catch<S>(onrejected: (reason: any) => S): Promise<S> {
this.schedule_async();
return this._promise.catch(onrejected);
catch<S>(onrejected: (reason: any) => S): Promise<S | T> {
return this._schedule_async().catch(onrejected);
}

finally(onfinally: () => void): Promise<T> {
this.schedule_async();
return this._promise.finally(onfinally);
return this._schedule_async().finally(onfinally);
}

schedule_sync(): this {
Expand Down
44 changes: 40 additions & 4 deletions src/transfer_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { generateUUID } from "./request_response";
import { createProxy, expose, wrap } from "./async_task";
import { FakeMessageChannel } from "./fake_message_channel";
import { ProxyMarked, proxyMarker } from "./types";
import * as ErrorStackParser from "error-stack-parser";

export const throwMarker = Symbol("Synclink.thrown");

Expand Down Expand Up @@ -103,6 +104,44 @@ type SerializedThrownValue =
| { isError: true; value: Error }
| { isError: false; value: unknown };

const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;

function renderChromeStack(s: ErrorStackParser.StackFrame): string {
const locationInfo = [s.fileName, s.lineNumber, s.columnNumber].join(":");
if (s.functionName) {
return ` at ${s.functionName} (${locationInfo})`;
} else {
return ` at ${locationInfo}`;
}
}

function renderFirefoxStack(s: ErrorStackParser.StackFrame): string {
const locationInfo = [s.fileName, s.lineNumber, s.columnNumber].join(":");
return `${s.functionName || ""}@${locationInfo}`;
}

function fixStackTrace(origError: Error): Error {
const e = new Error(origError.message);
e.name = origError.name;
const isChromeStack = origError.stack?.match(CHROME_IE_STACK_REGEXP);
const stackList = ErrorStackParser.parse(origError)
.concat(ErrorStackParser.parse(e))
.filter(
(s) =>
!s.fileName ||
!/synclink\.m?js$/.test(s.fileName) ||
s.functionName?.startsWith("SynclinkTask.") ||
s.functionName?.startsWith("then"),
)
.map(isChromeStack ? renderChromeStack : renderFirefoxStack);
if (isChromeStack) {
stackList.unshift(e.stack!.split("\n", 2)[0]);
}
const stack = stackList.join("\n");
e.stack = stack;
return e;
}

/**
* Internal transfer handler to handle thrown exceptions.
*/
Expand Down Expand Up @@ -130,10 +169,7 @@ export const throwTransferHandler: TransferHandler<
},
deserialize(serialized) {
if (serialized.isError) {
throw Object.assign(
new Error(serialized.value.message),
serialized.value,
);
throw fixStackTrace(serialized.value);
}
throw serialized.value;
},
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
"target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
"module": "esnext" /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
"lib": [
"esnext",
Expand Down

0 comments on commit cc08aab

Please sign in to comment.