From 88d1a49f18ad2bf2f8bdea5de6325dd6d6bef253 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Mon, 10 Apr 2017 18:04:07 +0900 Subject: [PATCH] fix(error): remove throw in Error constructor to improve performance in IE11 (#704) * fix(error): remove throw in Error constructor to improve performance in IE * fix(error): fix #698, don not generate long stack trace when Error.stackTraceLimit = 0, add null check --- lib/zone-spec/long-stack-trace.ts | 45 +++++++++++++------- lib/zone.ts | 10 ----- test/zone-spec/long-stack-trace-zone.spec.ts | 25 ++++++++++- 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/lib/zone-spec/long-stack-trace.ts b/lib/zone-spec/long-stack-trace.ts index 5c12460ea..87da027a3 100644 --- a/lib/zone-spec/long-stack-trace.ts +++ b/lib/zone-spec/long-stack-trace.ts @@ -58,7 +58,7 @@ function addErrorStack(lines: string[], error: Error): void { } function renderLongStackTrace(frames: LongStackTrace[], stack: string): string { - const longTrace: string[] = [stack.trim()]; + const longTrace: string[] = [stack ? stack.trim() : '']; if (frames) { let timestamp = new Date().getTime(); @@ -97,26 +97,38 @@ function renderLongStackTrace(frames: LongStackTrace[], stack: string): string { onScheduleTask: function( parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task): any { - const currentTask = Zone.currentTask; - let trace = currentTask && currentTask.data && (currentTask.data as any)[creationTrace] || []; - trace = [new LongStackTrace()].concat(trace); - if (trace.length > this.longStackTraceLimit) { - trace.length = this.longStackTraceLimit; + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + const currentTask = Zone.currentTask; + let trace = currentTask && currentTask.data && (currentTask.data as any)[creationTrace] || []; + trace = [new LongStackTrace()].concat(trace); + if (trace.length > this.longStackTraceLimit) { + trace.length = this.longStackTraceLimit; + } + if (!task.data) task.data = {}; + (task.data as any)[creationTrace] = trace; } - if (!task.data) task.data = {}; - (task.data as any)[creationTrace] = trace; return parentZoneDelegate.scheduleTask(targetZone, task); }, onHandleError: function( parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, error: any): boolean { - const parentTask = Zone.currentTask || error.task; - if (error instanceof Error && parentTask) { - const longStack = - renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); - try { - error.stack = (error as any).longStack = longStack; - } catch (err) { + if (Error.stackTraceLimit > 0) { + // if Error.stackTraceLimit is 0, means stack trace + // is disabled, so we don't need to generate long stack trace + // this will improve performance in some test(some test will + // set stackTraceLimit to 0, https://github.com/angular/zone.js/issues/698 + const parentTask = Zone.currentTask || error.task; + if (error instanceof Error && parentTask) { + const longStack = + renderLongStackTrace(parentTask.data && parentTask.data[creationTrace], error.stack); + try { + error.stack = (error as any).longStack = longStack; + } catch (err) { + } } } return parentZoneDelegate.handleError(targetZone, error); @@ -131,6 +143,9 @@ function captureStackTraces(stackTraces: string[][], count: number): void { } function computeIgnoreFrames() { + if (Error.stackTraceLimit <= 0) { + return; + } const frames: string[][] = []; captureStackTraces(frames, 2); const frames1 = frames[0]; diff --git a/lib/zone.ts b/lib/zone.ts index cfb40b18c..442c62ae5 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -1630,16 +1630,6 @@ const Zone: ZoneType = (function(global: any) { function ZoneAwareError(): Error { // We always have to return native error otherwise the browser console will not work. let error: Error = NativeError.apply(this, arguments); - if (!error.stack) { - // in IE, the error.stack will be undefined - // when error was constructed, it will only - // be available when throw - try { - throw error; - } catch (err) { - error = err; - } - } // Save original stack trace const originalStack = (error as any)['originalStack'] = error.stack; diff --git a/test/zone-spec/long-stack-trace-zone.spec.ts b/test/zone-spec/long-stack-trace-zone.spec.ts index cd3ba7b7c..7a6a144c9 100644 --- a/test/zone-spec/long-stack-trace-zone.spec.ts +++ b/test/zone-spec/long-stack-trace-zone.spec.ts @@ -103,7 +103,11 @@ describe('longStackTraceZone', function() { setTimeout(function() { let promise = new Promise(function(resolve, reject) { setTimeout(function() { - reject(new Error('Hello Promise')); + try { + throw new Error('Hello Promise'); + } catch (err) { + reject(err); + } }, 0); }); promise.catch(function(error) { @@ -116,4 +120,23 @@ describe('longStackTraceZone', function() { }, 0); }); }); + + it('should not produce long stack traces if Error.stackTraceLimit = 0', function(done) { + const originalStackTraceLimit = Error.stackTraceLimit; + lstz.run(function() { + setTimeout(function() { + setTimeout(function() { + setTimeout(function() { + if (log[0].stack) { + expectElapsed(log[0].stack, 1); + } + Error.stackTraceLimit = originalStackTraceLimit; + done(); + }, 0); + Error.stackTraceLimit = 0; + throw new Error('Hello'); + }, 0); + }, 0); + }); + }); });