Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upclass extends null with implicit constructor still broken #1036
Comments
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
allenwb
Nov 22, 2017
Member
In addition to the 10.a change, step 15 should be changed from
- If ClassHeritageopt is present, set F.[[ConstructorKind]] to "derived".
to
- If constructorParent is not the intrinsic object %FunctionPrototype%, set F.[[ConstructorKind]] to "derived".
Basically after step 10, the appropriate test to determine whether we are defining a base class is whether constructorParent is %FunctionPrototype%.
|
In addition to the 10.a change, step 15 should be changed from
to
Basically after step 10, the appropriate test to determine whether we are defining a base class is whether constructorParent is %FunctionPrototype%. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Kovensky
Dec 21, 2017
It's not just the default constructor that throws; an explicit construct also throws, at least as implemented in v8, JSC and SpiderMonkey:
new (class extends null {}) // throws because of implicit constructor
new (class extends null { constructor() {} }) // throws because of missing super call
new (class extends null { constructor() { super() } }) // throws because of super call
Kovensky
commented
Dec 21, 2017
•
|
It's not just the default constructor that throws; an explicit construct also throws, at least as implemented in v8, JSC and SpiderMonkey: new (class extends null {}) // throws because of implicit constructor
new (class extends null { constructor() {} }) // throws because of missing super call
new (class extends null { constructor() { super() } }) // throws because of super call |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
saschanaz
Dec 21, 2017
ChakraCore in MSEdge build 17063 allows the empty constructor() {} thing, not sure it's spec-compliant.
saschanaz
commented
Dec 21, 2017
|
ChakraCore in MSEdge build 17063 allows the empty |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
claudepache
Dec 21, 2017
Contributor
@Kovensky: It is possible to define a working constructor to a class extending null, provided that that constructor returns an explicit object, e.g.:
class C extends null { constructor() { return Object.create(new.target.prototype) } }|
@Kovensky: It is possible to define a working constructor to a class extending null, provided that that constructor returns an explicit object, e.g.: class C extends null { constructor() { return Object.create(new.target.prototype) } } |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
saschanaz
commented
Dec 22, 2017
•
|
Can we make |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
allenwb
Jan 24, 2018
Member
@claudepache Do you mean f3881fe ? You are probably right that they are observably equivalent. Probably because they both express my original intent.
The intent was that the values present at ClassDefinitionEvaluation determined whether a base class or a derived class was created and the form of default constructor that is emitted when one is needed. A class that doesn't have an extends clause or whose extends clause evaluated to null should be created as a Base class.
All other cases where the extends clause evaluates to a constructor function should be created as a Derived class.
It isn't relevant whether whether after the class is created somebody uses SetPrototypeOf to modify either the [[Prototype]] of the constructor or the [[Prototype]] or the prototype. This was agreed upon during ES6 development. It was concluded that it was hacker beware if they type to play proto games with constructors created using class definitions. Given that, I think
It was a bug ES2015 generated the wrong kind of default constructor and set extends null classes to derived. But it is by design that things are likely to break if you do proto hacking.
It isn't clear to me why f3881fe need to be reverted. Or my not to use the fix I have above. As either gives the original intended behavior which of buggy in ES2015.
It is kind of embarrassing that this feature has been broken for so long. It should really be fixed. I guess I think that this is the type of thing that the editor should be driving to resolution.
|
@claudepache Do you mean f3881fe ? You are probably right that they are observably equivalent. Probably because they both express my original intent. The intent was that the values present at ClassDefinitionEvaluation determined whether a base class or a derived class was created and the form of default constructor that is emitted when one is needed. A class that doesn't have an extends clause or whose extends clause evaluated to null should be created as a Base class. It isn't relevant whether whether after the class is created somebody uses SetPrototypeOf to modify either the [[Prototype]] of the constructor or the [[Prototype]] or the prototype. This was agreed upon during ES6 development. It was concluded that it was hacker beware if they type to play proto games with constructors created using class definitions. Given that, I think It was a bug ES2015 generated the wrong kind of default constructor and set extends null classes to derived. But it is by design that things are likely to break if you do proto hacking. It isn't clear to me why f3881fe need to be reverted. Or my not to use the fix I have above. As either gives the original intended behavior which of buggy in ES2015. It is kind of embarrassing that this feature has been broken for so long. It should really be fixed. I guess I think that this is the type of thing that the editor should be driving to resolution. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
saschanaz
Jan 24, 2018
this would be the first time we'd make it based on runtime values and not syntax whether something is a base class or subclass.
Allowing class extends void {} may be a syntactic way, although I'm not sure whether there is enough interest to add a new syntax for this feature.
saschanaz
commented
Jan 24, 2018
•
Allowing |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
anba
Mar 5, 2018
Contributor
@littledan Is this issue a blocker for the public/private fields proposal or is it acceptable if classes extending null won't support public/private fields?
|
@littledan Is this issue a blocker for the public/private fields proposal or is it acceptable if classes extending |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
littledan
Mar 5, 2018
Member
@anba You can get around this limitation with the following idiom:
class X {
field;
}
X.__proto__ = null;
X.prototype.__proto__ = null;The constructor will not call super(), and everything just works like it should. I think this workaround is cleaner than using Object.create manually in the constructor or anything like that that you'd have to do to get around the brokenness of classes that extend null.
For that reason, I'd say that this issue isn't a blocker. Still, I wouldn't mind seeing it resolved.
|
@anba You can get around this limitation with the following idiom: class X {
field;
}
X.__proto__ = null;
X.prototype.__proto__ = null;The constructor will not call For that reason, I'd say that this issue isn't a blocker. Still, I wouldn't mind seeing it resolved. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
anba
Mar 5, 2018
Contributor
@littledan I'd __proto__!
|
@littledan I'd |
allenwb commentedNov 21, 2017
•
edited
#781 reverted
class extends null {}to the ES2015 semantics and says:but there does not appear to be an issue for that "future work".
The original problem in ES2015 is that
new class extends null {}throws because the default constructor that is inserted does asuper()call to %FunctionPrototype% which is not a constructor.The guards in ES2015 to prevent inserting that
super()call were wrong and other subsequent attempts to correct that did things that cause other problems (see #781).I believe there is actually a simple spec. fix for this problem (referencing the ES2015 spec so things don't change out from under. Step 10.a currently is:
a. If ClassHeritageopt is present, then
the fix is:
a. If constructorParent is not the intrinsic object %FunctionPrototype%, then
Previous "fixes" tried to condition the implicit constructor choice on a null superclass value and/or other more global changes to the construction process. The new fix is a localized change that fixes the actual bug: generating a 'super()
call to %FunctionPrototype% which we know will fail because it is not a constructor. Also note that the above change is safe because:class extends Function.prototype ()` will independently throw.