Skip to content

Commit

Permalink
fix angular#595, refactor ZoneAwareError property copy
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaLiPassion committed Jan 13, 2017
1 parent e1d3240 commit 671a37b
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 27 deletions.
84 changes: 57 additions & 27 deletions lib/zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1330,38 +1330,66 @@ const Zone: ZoneType = (function(global: any) {
let frameParserStrategy = null;
const stackRewrite = 'stackRewrite';

const assignAll = function(to, from) {
if (!to) {
return to;
}
const createProperty = (key) => {
const name = __symbol__(key);
return {
configurable: true,
enumerable: true,
get: function() {
if (!this[name]) {
this[name] = this[__symbol__('error')][key];
}
return this[name];
},
set: function(value) {
this[name] = value;
}
};
};

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];
const createMethodProperty = (key) => {
return {
configurable: true,
enumerable: true,
writable: true,
value: function() {
const error = this[__symbol__('error')];
let errorMethod = error[key] || this[key];
if (errorMethod) {
return errorMethod.apply(error, arguments);
}
}
};
};

// 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];
}
const createErrorProperties = () => {
const props = Object.create(null);
const error = new NativeError();
let keys = Object.getOwnPropertyNames(error);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(error, key)) {
props[key] = createProperty(key);
}
}
const proto = NativeError.prototype;
if (proto) {
let pKeys = Object.getOwnPropertyNames(proto);
for (let i = 0; i < pKeys.length; i++) {
const key = pKeys[i];
// skip constructor
if (key !== 'constructor' && key !== 'toString' && key !== 'toSource') {
props[key] = createProperty(key);
}
}
}
return to;

props['originalStack'] = createProperty('originalStack');
props['zoneAwareStack'] = createProperty('zoneAwareStack');
props['toString'] = createMethodProperty('toString');
props['toSource'] = createMethodProperty('toSource');
return props;
};

/**
Expand All @@ -1378,6 +1406,7 @@ const Zone: ZoneType = (function(global: any) {
}
// Create an Error.
let error: Error = NativeError.apply(this, arguments);
this[__symbol__('error')] = error;

// Save original stack trace
error.originalStack = error.stack;
Expand Down Expand Up @@ -1414,11 +1443,12 @@ const Zone: ZoneType = (function(global: any) {
}
error.stack = error.zoneAwareStack = frames.join('\n');
}
return assignAll(this, error);
return this;
}

// Copy the prototype so that instanceof operator works as expected
ZoneAwareError.prototype = NativeError.prototype;
ZoneAwareError.prototype = Object.create(NativeError.prototype,
createErrorProperties());
ZoneAwareError[Zone.__symbol__('blacklistedStackFrames')] = blackListedStackFrames;
ZoneAwareError[stackRewrite] = false;

Expand Down
40 changes: 40 additions & 0 deletions test/common/Error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,46 @@ describe('ZoneAwareError', () => {
}
});

it('should not use child Error class get/set in ZoneAwareError constructor', () => {
// simulate @angular/facade/src/error.ts
class BaseError extends Error {
/** @internal **/
_nativeError: Error;

constructor(message: string) {
super(message);
const nativeError = new Error(message) as any as Error;
this._nativeError = nativeError;
}

get message() {
return this._nativeError.message;
}
set message(message) {
this._nativeError.message = message;
}
get name() {
return this._nativeError.name;
}
get stack() {
return (this._nativeError as any).stack;
}
set stack(value) {
(this._nativeError as any).stack = value;
}
toString() {
return this._nativeError.toString();
}
}

const func = () => {
const error = new BaseError('test');
expect(error.message).toEqual('test');
};

expect(func).not.toThrow();
});

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

0 comments on commit 671a37b

Please sign in to comment.