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 es-classes] Fix extending from ES Classes #16184

Closed

Conversation

pzuraq
Copy link
Contributor

@pzuraq pzuraq commented Jan 28, 2018

This PR seeks to fix the behavior of ES classes, specifically when calling .extend() on a native class. Currently, a new base class constructor (makeCtor) is made each time we call .extend(). With ES classes, this no longer works as expected because the base class does not ever call it's super constructor, breaking both the constructor of the native class and (in the future) class fields, which are assigned in the constructor.

The changes here detect if the class being extended from is actually a makeCtor, and if not provide a simple anonymous class that extends from the superclass instead. This allows the constructor to follow the prototype chain upward instead to whatever the last real makeCtor was, which then does all of the work of assigning properties, running proto, etc. In order to do this, metadata such as wasApplied had to be moved to the meta object for the object (see #15580) and static methods such as proto had to remove direct references to the class in makeCtor itself so they could be reused for upstream classes.

TODO:

  • Make sure this is the right strategy for fixing cross-compatibility
  • Cleanup code (some quirky/hacky things made it in)
  • Throw an error when trying to reopen native classes
  • More tests
    • Testing reopen and reopenClass, particularly when they retrigger a proto
    • Testing reopen and reopenClass fail on native classes
  • Perf benchmarks

@rwjblue rwjblue requested a review from krisselden January 28, 2018 03:02
let initProperties, initFactory;

class Class {
constructor() {
let self = this;

if (!wasApplied) {
Class.proto(); // prepare prototype...
if (!meta(this).parent.wasApplied) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a bit of a hack, I found that in some cases (particularly ObjectProxy) the following would be true:

let m1 = meta(this);
this.constructor.proto();
let m2 = meta(this);

m1 !== m2

For some reason, proto can confuse meta this way (something to do with the way WeakMap links between objects and values?). On the otherside, if we were to do:

meta(Object.getPrototypeFor(this)).wasApplied

The meta for a native class does not have proto set (it was created outside of extend, no chance to add it) so the check in the getter/setter for wasApplied fails.

I think ideally we only load meta once, and just use the parent value.

@rwjblue
Copy link
Member

rwjblue commented Jan 28, 2018

Make sure this is the right strategy for fixing cross-compatibility

I believe that this is indeed going down the right path...

@rwjblue
Copy link
Member

rwjblue commented Feb 1, 2018

@pzuraq - Mind rebasing on top of the "single constructor arg" work?

@pzuraq pzuraq force-pushed the experiment/class-constructor-fix branch from 8b91e86 to 5fac5bb Compare February 1, 2018 20:04
@pzuraq
Copy link
Contributor Author

pzuraq commented Feb 1, 2018

Rebased! I should have some time later on this week to work on this so let me if there are any specific changes I should make. I want to try to get rid of the meta() twice bug (or at least find the root cause), other than that I think this is in a pretty good spot actually (just needs more tests)

@rwjblue rwjblue mentioned this pull request Feb 24, 2018
2 tasks
@pzuraq pzuraq force-pushed the experiment/class-constructor-fix branch 2 times, most recently from 5b1a7fd to b89d223 Compare March 8, 2018 21:04
@pzuraq
Copy link
Contributor Author

pzuraq commented Mar 8, 2018

Alright, this is ready for review! Turned out the meta bug was not a bug, meta changes because we wrap self in a proxy (which makes sense).

Here are the perf benchmarks, taken just before I added some tests (will redo them with the current git hash shortly). I ran 100 samples, and unfortunately it does look like this is a minor regression:

results.pdf
results.json.txt

@krisselden
Copy link
Contributor

krisselden commented Mar 24, 2018

@pzuraq that isn't a significant result, it has too high of a chance of incorrectly rejecting the null hypothesis.

@pzuraq
Copy link
Contributor Author

pzuraq commented Mar 25, 2018

Alright, so should I rerun with more samples? Or is this good enough to merge?

Currently on vacation but I’ll get it rebased as soon as I get back!

@rwjblue rwjblue force-pushed the experiment/class-constructor-fix branch from b89d223 to 40c2eb2 Compare March 26, 2018 23:23
@rwjblue
Copy link
Member

rwjblue commented Mar 26, 2018

FYI - I just rebased it (@krisselden and I are poking at this stuff, so figured I'd rebase while I was in there...)

@pzuraq
Copy link
Contributor Author

pzuraq commented Mar 27, 2018

👍 I can look into the test failures as soon as I’m back next week

@pzuraq
Copy link
Contributor Author

pzuraq commented Apr 3, 2018

This has been superceded by a better strategy implemented in #16436

@pzuraq pzuraq closed this Apr 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants