Fix #546, #554, #555, the error should keep prototype chain and can be called without new#559
Conversation
…otype chain and can be called without new
|
|
||
| // Copy the prototype so that instanceof operator works as expected | ||
| ZoneAwareError.prototype = NativeError.prototype; | ||
| ZoneAwareError.prototype = Object.create(NativeError.prototype); |
There was a problem hiding this comment.
Why is Object.create necessary here? What is wrong with having it the way it was before?
There was a problem hiding this comment.
I just want to create a new object to let ZoneAwareError.prototype be different with NativeError's, so in the future, if someday modify ZoneAwareError's prototype will not impact NativeError's, for this issue #546, we don't need to use Object.create
| } | ||
| return error; | ||
| } | ||
| return (this && this !== global) ? this : error; |
There was a problem hiding this comment.
The fact that sometimes we return this and sometimes errror concerns me. We have two seperate code path and I fear that things will only work in one or the other code path.
There was a problem hiding this comment.
Yes, it is a little risky code, I will modify it.
| // Create an Error. | ||
| let error: Error = NativeError.apply(this, arguments); | ||
|
|
||
| if (this && this !== global) { |
There was a problem hiding this comment.
I think a better strategy would be to always assume that we were called without the new operator, that way we don't have to decide.
There was a problem hiding this comment.
Yes, I understand your concern, you are right. I will change it.
|
@mhevery , Thank you for your review, I'll consider it again and reply to you soon. |
|
found another problem, In typescript 2.0.8, the class extends will be compiled into var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var MyError = (function (_super) {
__extends(MyError, _super);
function MyError() {
_super.apply(this, arguments);
}
return MyError;
}(Error));This is not correct, In typescript 2.1.4 , it will be compiled into var MyError = (function (_super) {
__extends(MyError, _super);
function MyError() {
return _super.apply(this, arguments) || this;
}
return MyError;
}(Error));This is the correct one. |
|
@mhevery , I have changed my PR, please review it is correct or not, Thanks a lot. |
| "ts-loader": "^0.6.0", | ||
| "tslint": "^3.15.1", | ||
| "tslint-eslint-rules": "^3.1.0", | ||
| "typescript": "^2.0.2", |
There was a problem hiding this comment.
Let's not upgrade to TS 2.1.4 since we have to make sure that the generated .d.ts is compatible with angular which is on alder version on TS. The easiest is to wait until angular is ready.
There was a problem hiding this comment.
Ok, I see, I will rollback the typescript version.
| // and not global (non-strict mode call Error()) | ||
| // and it is an error not an unrelated object | ||
| if (this && this !== global && this instanceof Error) { | ||
| error = setPrototypeOf(error, getPrototypeOf(this)); |
There was a problem hiding this comment.
Calling setProtypeOf will make this object slow. Not sure if that is a problem since it is a Error object.
There was a problem hiding this comment.
Thank you for pointing out this one, I didn't realize that setPrototypeOf will make javascript engine optimization become slow. how about change the code above to
// if this is valid, means not undefined
// and not global (non-strict mode call Error())
// and it is an error not an unrelated object
if (this && this !== global && this instanceof Error) {
assignAll(this, error);
return this;
}-Pro: don't use setPrototypeOf
-Con:if/else will return different object
I just make a new commit to describe it, please review.
| // and it is an error not an unrelated object | ||
| if (this && this !== global && this instanceof Error | ||
| && Object.getPrototypeOf(this) !== NativeError.prototype && assignAll(this, error)) { | ||
| if (this && this !== global && this instanceof Error && |
There was a problem hiding this comment.
I was thinking about this some more, and I think the best way to do this would be to place this guard at the top of the function
if (! this instanceof ZoneAwareError) {
return ZoneAwareError.apply(Object.create(ZoneAwareError), arguments);
}
This way we force the new and only have one code path. What do you think?
There was a problem hiding this comment.
Thank you for the review, in my understanding, your code will guarantee that 'this' is a valid ZoneAwareError, and we can set all properties from error to this and then return this. Is my understanding correct?
I have modified the PR based on your idea, please review. Thank you.
|
Thanks for all of your hard work. |
With the commit #547, the #546, error prototype issue was fixed, but a bigger issue occurs, that
Error object can be created with direct call.
let error = Error('message');
And the #547 PR will break it, cause a lot of errors, such as #554, #555
Sorry for the buggy commit in #547.
In this new PR, I checked the this exists or not and checked the this is global or not (non strict mode). So it should fix both #546 and #554, #555.
Please review, Thank you.