diff --git a/lib/zone.ts b/lib/zone.ts index cf7eb0a8c..88925b411 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -1747,7 +1747,7 @@ const Zone: ZoneType = (function(global: any) { 'long-stack-trace' ]; - function attachZoneAndRemoveInternalZoneFrames(error: any) { + function attachZoneAndRemoveInternalZoneFrames(error: any, zoneAwareError: any) { // Save original stack trace error.originalStack = error.stack; // Process the stack trace and rewrite the frames. @@ -1789,7 +1789,14 @@ const Zone: ZoneType = (function(global: any) { } } } - error.stack = error.zoneAwareStack = frames.join('\n'); + const finalStack: string = frames.join('\n'); + try { + error.stack = error.zoneAwareStack = finalStack; + } catch (nonWritableErr) { + // in some browser, the error.stack is readonly such as PhantomJS + // so we need to store the stack frames to zoneAwareError directly + zoneAwareError.stack = finalStack; + } } } @@ -1820,7 +1827,7 @@ const Zone: ZoneType = (function(global: any) { this[__symbol__('error')] = error; // 1. attach zone information to stack frame // 2. remove zone internal stack frames - attachZoneAndRemoveInternalZoneFrames(error); + attachZoneAndRemoveInternalZoneFrames(error, this); // use defineProperties here instead of copy property value // because of issue #595 which will break angular2. diff --git a/test/common/Error.spec.ts b/test/common/Error.spec.ts index 17f33cd86..ce748bc66 100644 --- a/test/common/Error.spec.ts +++ b/test/common/Error.spec.ts @@ -405,4 +405,24 @@ describe('Error stack', () => { }, null, () => null, null); task.invoke(); })); + + it('should be able to generate zone free stack even NativeError stack is readonly', function() { + const _global: any = + typeof window === 'object' && window || typeof self === 'object' && self || global; + const NativeError = _global['__zone_symbol__Error']; + const desc = Object.getOwnPropertyDescriptor(NativeError.prototype, 'stack'); + if (desc) { + const originalSet: (value: any) => void = desc.set; + // make stack readonly + desc.set = null; + + try { + const error = new Error('test error'); + expect(error.stack).toBeTruthy(); + assertStackDoesNotContainZoneFrames(error); + } finally { + desc.set = originalSet; + } + } + }); });