Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions lib/zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1310,12 +1310,52 @@ const Zone: ZoneType = (function(global: any) {
let frameParserStrategy = null;
const stackRewrite = 'stackRewrite';

const assignAll = function(to, from) {
if (!to) {
return to;
}

if (from) {
let keys = Object.getOwnPropertyNames(from);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(from, key)) {
to[key] = from[key];
}
}

// copy all properties from prototype
// in Error, property such as name/message is in Error's prototype
// but not enumerable, so we copy those properties through
// Error's prototype
const proto = Object.getPrototypeOf(from);
if (proto) {
let pKeys = Object.getOwnPropertyNames(proto);
for (let i = 0; i < pKeys.length; i++) {
const key = pKeys[i];
// skip constructor
if (key !== 'constructor') {
to[key] = from[key];
}
}
}
}
return to;
};

/**
* This is ZoneAwareError which processes the stack frame and cleans up extra frames as well as
* adds zone information to it.
*/
function ZoneAwareError() {
// make sure we have a valid this
// if this is undefined(call Error without new) or this is global
// or this is some other objects, we should force to create a
// valid ZoneAwareError by call Object.create()
if (!(this instanceof ZoneAwareError)) {
return ZoneAwareError.apply(Object.create(ZoneAwareError.prototype), arguments);
}
// Create an Error.
let error: Error = NativeError.apply(this, arguments);

Expand Down Expand Up @@ -1354,7 +1394,7 @@ const Zone: ZoneType = (function(global: any) {
}
error.stack = error.zoneAwareStack = frames.join('\n');
}
return error;
return assignAll(this, error);
}

// Copy the prototype so that instanceof operator works as expected
Expand Down Expand Up @@ -1394,8 +1434,6 @@ const Zone: ZoneType = (function(global: any) {
}
});

// Now we need to populet the `blacklistedStackFrames` as well as find the

// Now we need to populet the `blacklistedStackFrames` as well as find the
// run/runGuraded/runTask frames. This is done by creating a detect zone and then threading
// the execution through all of the above methods so that we can look at the stack trace and
Expand Down
56 changes: 56 additions & 0 deletions test/common/Error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,62 @@ describe('ZoneAwareError', () => {
// and there is no point in running them.
if (!Error['stackRewrite']) return;

it('should keep error prototype chain correctly', () => {
class MyError extends Error {}
const myError = new MyError();
expect(myError instanceof Error).toBe(true);
expect(myError instanceof MyError).toBe(true);
expect(myError.stack).not.toBe(undefined);
});

it('should instanceof error correctly', () => {
let myError = Error('myError');
expect(myError instanceof Error).toBe(true);
let myError1 = Error.call(undefined, 'myError');
expect(myError1 instanceof Error).toBe(true);
let myError2 = Error.call(global, 'myError');
expect(myError2 instanceof Error).toBe(true);
let myError3 = Error.call({}, 'myError');
expect(myError3 instanceof Error).toBe(true);
let myError4 = Error.call({test: 'test'}, 'myError');
expect(myError4 instanceof Error).toBe(true);
});

it('should return error itself from constructor', () => {
class MyError1 extends Error {
constructor() {
const err: any = super('MyError1');
this.message = err.message;
}
}
let myError1 = new MyError1();
expect(myError1.message).toEqual('MyError1');
expect(myError1.name).toEqual('Error');
});

it('should return error by calling error directly', () => {
let myError = Error('myError');
expect(myError.message).toEqual('myError');
let myError1 = Error.call(undefined, 'myError');
expect(myError1.message).toEqual('myError');
let myError2 = Error.call(global, 'myError');
expect(myError2.message).toEqual('myError');
let myError3 = Error.call({}, 'myError');
expect(myError3.message).toEqual('myError');
});

it('should have browser specified property', () => {
let myError = new Error('myError');
if (Object.prototype.hasOwnProperty.call(Error.prototype, 'description')) {
// in IE, error has description property
expect((<any>myError).description).toEqual('myError');
}
if (Object.prototype.hasOwnProperty.call(Error.prototype, 'fileName')) {
// in firefox, error has fileName property
expect((<any>myError).fileName).toContain('zone');
}
});

it('should show zone names in stack frames and remove extra frames', () => {
const rootZone = getRootZone();
const innerZone = rootZone.fork({name: 'InnerZone'});
Expand Down