feat: support getters and setters in a class body - #60
Merged
Conversation
Object literals took accessors and classes did not, so the class form was a
syntax error:
class C { get g() { return 2; } } Expected ( but found g
class C { set s(v) { this._v = v; } } Expected ( but found s
class C { static get g() { return 3; } } Expected ( but found g
Plain and static methods already worked; only the accessor form was missing.
The member loop now recognises get and set the way an object literal does, and
reuses propertyGetterFunction and propertySetterFunction to read them, so the
parsing rules - property name, empty getter parameter list, sloppy setter
parameter - are the same in both places.
A method lowers to an assignment on the prototype, or on the class itself when
static. An accessor is not expressible as an assignment, and unlike an object
literal member there is no literal here to carry it, so it lowers to
Object.defineProperty on the same target, with configurable true and enumerable
false. Defining one at a time is correct for a get/set pair: a descriptor only
changes the fields it names, so a setter defined afterwards keeps the getter
beside it.
"get" and "set" stay usable as ordinary member names. What follows the keyword
decides, as it does in an object literal: a property name makes it an accessor,
a parameter list makes it a method. class C { get() {} } and static get() {}
both still parse as methods.
Two things worth recording:
Object.defineProperty is read from the global scope, so a class body evaluated
where Object is shadowed would not find it. That is the same shape every ES6 to
ES5 lowering of accessors takes, and it adds no runtime helper.
An accessor is correctly non-enumerable. A method, on the prototype by plain
assignment, is still enumerable, which ES6 says it should not be. That is
pre-existing and not changed here.
./gradlew build testOptimistic testPessimistic:
suite before after
test 666, 0 fail 666, 0 fail
testOptimistic 1718, 0 fail 1718, 0 fail
testPessimistic 1718, 0 fail 1718, 0 fail
marevol
force-pushed
the
es6/class-accessors
branch
from
August 28, 2026 05:11
bc0e581 to
284d022
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #59.
Object literals took accessors and classes did not, so the class form was a syntax error:
Plain and static methods already worked; only the accessor form was missing.
Parsing
The member loop now recognises
getandsetthe way an object literal does, and reusespropertyGetterFunctionandpropertySetterFunctionto read them, so the parsing rules — property name, empty getter parameter list, sloppy setter parameter — are the same in both places.getandsetstay usable as ordinary member names. What follows the keyword decides, as it does in an object literal: a property name makes it an accessor, a parameter list makes it a method.class C { get() {} }andstatic get() {}both still parse as methods.Lowering
A method lowers to an assignment on the prototype, or on the class itself when static. An accessor is not expressible as an assignment, and unlike an object literal member there is no literal here to carry it, so it lowers to
Object.definePropertyon the same target, withconfigurable: trueandenumerable: false.Defining one at a time is correct for a get/set pair: a descriptor only changes the fields it names, so a setter defined afterwards keeps the getter beside it.
Two things worth recording
Object.definePropertyis read from the global scope, so a class body evaluated whereObjectis shadowed would not find it. That is the same shape every ES6→ES5 lowering of accessors takes, and it adds no runtime helper.An accessor is correctly non-enumerable. A method, on the prototype by plain assignment, is still enumerable, which ES6 says it should not be. That is pre-existing and not changed here.
Verification
./gradlew build testOptimistic testPessimistic