Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] [BUGFIX release] Ensure CP's are _super wrapped. #13242

Closed
wants to merge 2 commits into from

Conversation

rwjblue
Copy link
Member

@rwjblue rwjblue commented Apr 4, 2016

Prior to this change, a CP that was not included in the superclass would not get re-wrapped. This means that when calling it from another function that is required to be super wrapped would set that other function as the _super.

This changes to ensure that all descriptors are wrapped if they include _super (hasSuper check on platforms that support Function.prototype.toString).

Fixes #13230.

/cc @stefanpenner

@stefanpenner
Copy link
Member

Lgtm, @krisselden r?

@mixonic
Copy link
Member

mixonic commented Apr 4, 2016

This seems reasonable.

@@ -102,8 +102,14 @@ function giveDescriptorSuper(meta, key, property, values, descs, base) {
superProperty = superDesc;
}

// add a default `_super` to ensure proper wraping if `_super` is used in `property`
Copy link
Member

Choose a reason for hiding this comment

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

wrapping

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch! Updated...

Prior to this change, a CP that was not included in the superclass would
not get re-wrapped. This means that when calling it from another function
that is required to be super wrapped would set that other function as
the `_super`.

This changes to ensure that all descriptors are wrapped if they include
`_super` (`hasSuper` check on platforms that support
`Function.prototype.toString`).
@netes
Copy link

netes commented Apr 4, 2016

👍 for this one

Prior to this, if a method was not included in the superclasses
prototype, we would skip wrapping it with its own `_super`. However,
that means that if the method is called from within another `_super`
wrapped method it would not properly reset the `_super` causing
incorrect results.

This ensures that all methods are super wrapped if they contain
`_super` (and `Function.prototype.toString` is present).
@rwjblue
Copy link
Member Author

rwjblue commented Apr 4, 2016

This also was an issue for normal methods (aka non-CP's). I fixed that and added an additional test for it.

@rwjblue
Copy link
Member Author

rwjblue commented Apr 4, 2016

OK, so forcing wrapping of all functions that match the hasSuper check causes issues for any classes on the object.

The following breaks:

export default Ember.Object.extend({
  baseModel: Ember.Object.extend()
});

One specific example is glimmer templates since they are now a class (and get instantiated) by the container, where you would have Ember.Component.extend({ layout: compile('foo') }) (and layout would get super wrapped causing it to loose the .create / .extend methods on the class).

@rwjblue
Copy link
Member Author

rwjblue commented Apr 4, 2016

I can remove the last commit and land the fix for CP's (which seems less of a compat concern), but we need to decide what to do about the test added for normal method's:

let A = Ember.Object.extend({
  foo() { return 'A - Foo'; }
});

let B = A.extend({
  bar() {
    return this._super(...arguments) + '| B - Bar';
  },

  foo() {
    return this._super(...arguments) + ' | B - Foo | ' + this.bar();
  }
});
let b = B.create()
b.foo();

// => "A - Foo | B - Foo | A - Foo| B - Bar"

When B.prototype.bar calls this._super() it gets A.prototype.foo...

Runnable Demo


// only provide a super setter if the property has one already
if (property._setter) {
superProperty._setter = function() {};
Copy link
Contributor

Choose a reason for hiding this comment

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

you should use the ROOT that wrap uses for this, so it is marked as not having super explicitly

@krisselden
Copy link
Contributor

It is non obvious to me that you should be able to call super without having a method you are overriding. I know we handled a case like this for mixins, after talking with @wycats this should have just been an error. I can't think of another language where you can call super without actually overriding anything.

Also, the fix to hasSuper for CP macros I think was wrong, and I'm nervous about how many things will be wrapped by this PR, it seems like instead of the REGEX being loosened so much, that the macros themselves should have likely just passed generatedFn.hasSuper = hasSuper(callback) anyway, I think at a minimum we should consider changing the auto terminal super behavior to be deprecated and warn in this case.

@rwjblue
Copy link
Member Author

rwjblue commented Apr 4, 2016

@krisselden - Thanks for reviewing!

It is non obvious to me that you should be able to call super without having a method you are overriding.

If that is true we should error not silently ignore/swallow the this._super call.

Also, the fix to hasSuper for CP macros I think was wrong

Which fix?

I think at a minimum we should consider changing the auto terminal super behavior to be deprecated and warn in this case.

I'm not certain what exactly this means, but we can discuss the first point on the next core team call to figure out a path forward.

@krisselden
Copy link
Contributor

@rwjblue it means the original test case that @mixonic did for Mixin.create() calling a _super and being mixed into something that did not have that, that was covering the same issue but when it was set to itself so it infinitely looped.

@krisselden
Copy link
Contributor

@rwjblue the fix I was referring to with CPs was loosening the REGEX for hasSuper to _super or invoking something that could call super. Anyway, that loosening is what makes it difficult to fix the above case without hurting props that aren't methods, but even then, tightening it wouldn't fix it if toString() wasn't present. So I guess it doesn't matter. Props that are functions where we don't want them wrapped is a problem that our syntax doesn't allow us to really disambiguate.

@rwjblue
Copy link
Member Author

rwjblue commented Apr 10, 2016

We had a long discussion about this during the last core team meeting (see notes here). The basic conclusions are:

  • We should not support calling _super when the parent class does not have a method defined (aka when there is no _super). This likely means adding a deprecation for now, and converting that to an error in 3.0.0.
  • We should ensure that all framework hooks have a default implementation present (to avoid the error mentioned above).

Will continue to follow up with @krisselden and @wycats to figure out the right path forward for this...

@offirgolan
Copy link

Thank you for taking the time to figure this out fellas. As of right now, is there a current workaround to check if a CP has a super?

@rwjblue
Copy link
Member Author

rwjblue commented Apr 14, 2016

@offirgolan - Not really. I suppose you could do something like this, but it pretty much sucks.

@offirgolan
Copy link

@rwjblue Yeah I see what you mean. I guess it could be an intermediate solution but if you are going to deprecate calling _super when the parent class doesn't have the method defined, I feel like there should be a helper method exposed to check that.

@rwjblue
Copy link
Member Author

rwjblue commented Apr 14, 2016

@offirgolan - I think we will likely warn/deprecate/something. As @krisselden mentioned, most programming languages do not allow calling super when there is no super, so I suppose this is basically the same thing...

@offirgolan
Copy link

@rwjblue Ah okay totally understandable. Thanks for the clarification 😄

@rwjblue rwjblue changed the title [BUGFIX release] Ensure CP's are _super wrapped. [WIP] [BUGFIX release] Ensure CP's are _super wrapped. Apr 18, 2016
@rwjblue
Copy link
Member Author

rwjblue commented Jan 3, 2017

Closing in favor of #14780.

@rwjblue rwjblue closed this Jan 3, 2017
@rwjblue rwjblue deleted the ensure-super-is-reset branch January 3, 2017 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants