Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
fix: stack rewriting now works with source maps
Browse files Browse the repository at this point in the history
In Chrome for the source maps to work the object must be an instance of
native `Error`. This means that we can’t return a subclass of `Error`
or error handling in the dev console will not work properly.

In addition the stack frames must have a certain format or the source
mapping is disabled. For this reason we have changed the long stack
format to conform to that shape.
  • Loading branch information
mhevery committed Mar 16, 2017
1 parent 0e19304 commit bcd09a0
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 396 deletions.
2 changes: 1 addition & 1 deletion lib/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const set = 'set';
const clear = 'clear';
const blockingMethods = ['alert', 'prompt', 'confirm'];
const _global: any =
typeof window === 'object' && window || typeof self === 'object' && self || global;
typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global;

patchTimer(_global, set, clear, 'Timeout');
patchTimer(_global, set, clear, 'Interval');
Expand Down
3 changes: 2 additions & 1 deletion lib/common/timers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ export function patchTimer(window: any, setName: string, cancelName: string, nam

function scheduleTask(task: Task) {
const data = <TimerOptions>task.data;
data.args[0] = function() {
function timer() {
try {
task.invoke.apply(this, arguments);
} finally {
delete tasksByHandleId[data.handleId];
}
};
data.args[0] = timer;
data.handleId = setNative.apply(window, data.args);
tasksByHandleId[data.handleId] = task;
return task;
Expand Down
3 changes: 2 additions & 1 deletion lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
declare const WorkerGlobalScope: any;

export const zoneSymbol: (name: string) => string = (n) => `__zone_symbol__${n}`;
const _global: any = typeof window === 'object' && window || typeof self === 'object' && self || global;
const _global: any =
typeof window === 'object' && window || typeof self === 'object' && self || global;

export function bindArguments(args: any[], source: string): any[] {
for (let i = args.length - 1; i >= 0; i--) {
Expand Down
63 changes: 24 additions & 39 deletions lib/zone-spec/long-stack-trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@
*/

const NEWLINE = '\n';
const SEP = ' ------------- ';
const IGNORE_FRAMES: string[] = [];
const IGNORE_FRAMES: {[k: string]: true} = {};
const creationTrace = '__creationTrace__';
const ERROR_TAG = 'STACKTRACE TRACKING';
const SEP_TAG = '__SEP_TAG__';
let sepTemplate = '';

class LongStackTrace {
error: Error = getStacktrace();
timestamp: Date = new Date();
}

function getStacktraceWithUncaughtError(): Error {
return new Error('STACKTRACE TRACKING');
return new Error(ERROR_TAG);
}

function getStacktraceWithCaughtError(): Error {
Expand Down Expand Up @@ -49,22 +51,24 @@ function addErrorStack(lines: string[], error: Error): void {
for (let i = 0; i < trace.length; i++) {
const frame = trace[i];
// Filter out the Frames which are part of stack capturing.
if (!(i < IGNORE_FRAMES.length && IGNORE_FRAMES[i] === frame)) {
if (!IGNORE_FRAMES.hasOwnProperty(frame)) {
lines.push(trace[i]);
}
}
}

function renderLongStackTrace(frames: LongStackTrace[], stack: string): string {
const longTrace: string[] = [stack];
const longTrace: string[] = [stack.trim()];

if (frames) {
let timestamp = new Date().getTime();
for (let i = 0; i < frames.length; i++) {
const traceFrames: LongStackTrace = frames[i];
const lastTime = traceFrames.timestamp;
longTrace.push(
`${SEP} Elapsed: ${timestamp - lastTime.getTime()} ms; At: ${lastTime} ${SEP}`);
let separator =
`____________________Elapsed ${timestamp - lastTime.getTime()} ms; At: ${lastTime}`;
separator = separator.replace(/[^\w\d]/g, '_');
longTrace.push(sepTemplate.replace(SEP_TAG, separator));
addErrorStack(longTrace, traceFrames.error);

timestamp = lastTime.getTime();
Expand Down Expand Up @@ -105,42 +109,15 @@ function renderLongStackTrace(frames: LongStackTrace[], stack: string): string {
},

onHandleError: function(
parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, error: any): any {
parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, error: any): boolean {
const parentTask = Zone.currentTask || error.task;
if (error instanceof Error && parentTask) {
let stackSetSucceeded: string|boolean = null;
const longStack =
renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack);
try {
let descriptor = Object.getOwnPropertyDescriptor(error, 'stack');
if (descriptor && descriptor.configurable) {
const delegateGet = descriptor.get;
const value = descriptor.value;
descriptor = {
get: function() {
return renderLongStackTrace(
parentTask.data && parentTask.data[creationTrace],
delegateGet ? delegateGet.apply(this) : value);
}
};
Object.defineProperty(error, 'stack', descriptor);
stackSetSucceeded = true;
}
error.stack = (error as any).longStack = longStack;
} catch (err) {
}
const longStack: string = stackSetSucceeded ?
null :
renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack);
if (!stackSetSucceeded) {
try {
stackSetSucceeded = error.stack = longStack;
} catch (err) {
}
}
if (!stackSetSucceeded) {
try {
stackSetSucceeded = (error as any).longStack = longStack;
} catch (err) {
}
}
}
return parentZoneDelegate.handleError(targetZone, error);
}
Expand All @@ -161,11 +138,19 @@ function computeIgnoreFrames() {
for (let i = 0; i < frames1.length; i++) {
const frame1 = frames1[i];
const frame2 = frames2[i];
if (!sepTemplate && frame1.indexOf(ERROR_TAG) == -1) {
sepTemplate = frame1.replace(/^(\s*(at)?\s*)([\w\/\<]+)/, '$1' + SEP_TAG);
}
if (frame1 === frame2) {
IGNORE_FRAMES.push(frame1);
IGNORE_FRAMES[frame1] = true;
} else {
break;
}
console.log('>>>>>>', sepTemplate, frame1);
}
if (!sepTemplate) {
// If we could not find it default to this text.
sepTemplate = SEP_TAG + '@[native code]';
}
}
computeIgnoreFrames();
Loading

0 comments on commit bcd09a0

Please sign in to comment.