Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Fix #546, #554, #555, the error should keep prototype chain and can be called without new #559

Merged
merged 20 commits into from
Jan 6, 2017

Conversation

JiaLiPassion
Copy link
Collaborator

@JiaLiPassion JiaLiPassion commented Dec 20, 2016

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.

…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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is Object.create necessary here? What is wrong with having it the way it was before?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is a little risky code, I will modify it.

@@ -1318,13 +1318,24 @@ const Zone: ZoneType = (function(global: any) {
function ZoneAwareError() {
// Create an Error.
let error: Error = NativeError.apply(this, arguments);

if (this && this !== global) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I understand your concern, you are right. I will change it.

@JiaLiPassion
Copy link
Collaborator Author

@mhevery , Thank you for your review, I'll consider it again and reply to you soon.

@JiaLiPassion
Copy link
Collaborator Author

JiaLiPassion commented Dec 22, 2016

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.
And it will behave just like the error case in #546.

@JiaLiPassion
Copy link
Collaborator Author

JiaLiPassion commented Dec 23, 2016

@mhevery , I have changed my PR, please review it is correct or not, Thanks a lot.
The package.json was updated because typescript 2.0.8 will treat 'class extends' in a wrong way.

@@ -69,7 +69,7 @@
"ts-loader": "^0.6.0",
"tslint": "^3.15.1",
"tslint-eslint-rules": "^3.1.0",
"typescript": "^2.0.2",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling setProtypeOf will make this object slow. Not sure if that is a problem since it is a Error object.

Copy link
Collaborator Author

@JiaLiPassion JiaLiPassion Jan 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@@ -1387,8 +1387,8 @@ const Zone: ZoneType = (function(global: any) {
// 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
&& Object.getPrototypeOf(this) !== NativeError.prototype && assignAll(this, error)) {
if (this && this !== global && this instanceof Error &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@mhevery
Copy link
Contributor

mhevery commented Jan 6, 2017

Thanks for all of your hard work.

@mhevery mhevery merged commit 82722c3 into angular:master Jan 6, 2017
@JiaLiPassion JiaLiPassion deleted the issue-555 branch January 7, 2017 00:29
@JiaLiPassion JiaLiPassion restored the issue-555 branch January 12, 2017 01:25
@JiaLiPassion JiaLiPassion deleted the issue-555 branch January 12, 2017 01:25
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants