-
-
Notifications
You must be signed in to change notification settings - Fork 258
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woo!
ast/spec.md
Outdated
type: "ClassPrivateMethod"; | ||
key: Identifier; | ||
kind: "method" | "get" | "set"; | ||
computed: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't be computed.
ast/spec.md
Outdated
kind: "method" | "get" | "set"; | ||
computed: boolean; | ||
static: boolean; | ||
decorators: [ Decorator ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need async
and generator
flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would that be needed for the normal ClassMethod
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup.
ast/spec.md
Outdated
@@ -1049,6 +1050,19 @@ interface ClassMethod <: Function { | |||
} | |||
``` | |||
|
|||
## ClassMethod |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to ClassPrivateMethod
src/parser/expression.js
Outdated
parseClassPrivateName( | ||
prop: N.ClassPrivateProperty | N.ClassPrivateMethod, | ||
): N.Expression { | ||
prop.computed = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's delete this.
src/parser/statement.js
Outdated
// eaten the #, but in other cases we haven't. How to solve? | ||
this.expectPlugin("classPrivateMethods"); | ||
// private "normal" method | ||
method.kind = "method"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we hoist this to the beginning of the this.isClassMethod()
if statement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confused about where exactly you want this part to be moved to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before the if (isPrivate)
.
src/parser/statement.js
Outdated
const isPrivate = this.eat(tt.hash); | ||
|
||
if (isPrivate) { | ||
this.expectOnePlugin(["classPrivateProperties", "classPrivateMethods"]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to do this before eating the hash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/parser/statement.js
Outdated
if (this.match(tt.hash)) { | ||
// private getter/setter | ||
this.expectPlugin("classPrivateMethods"); | ||
// The private # wouldn't have been eaten as the "async" was in front of it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment is incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh? Maybe my wording was confusing.
What I'm trying to say is that it's now being eaten, but it wasn't detected as being "private" in the code above (let isPrivate = ...
). Is that wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "async" part is incorrect. We're in the "get"/"set" path here.
src/parser/statement.js
Outdated
this.expectPlugin("classPrivateMethods"); | ||
// The private # wouldn't have been eaten as the "async" was in front of it. | ||
this.next(); | ||
// The so-called parsed name would have been "async": get the real name. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment again.
src/parser/statement.js
Outdated
false, | ||
/* isConstructor */ false, | ||
); | ||
this.checkGetterSetterParamCount(method); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should apply to private accessors, too.
src/types.js
Outdated
ClassMethodOrDeclareMethodCommon & { | ||
type: "ClassPrivateMethod", | ||
key: Identifier, | ||
static: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be boolean
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need static
in the first place? If not, how would I handle that in terms of types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, it's already in ClassMethodOrDeclareMethodCommon
, so we can just drop this line.
src/parser/statement.js
Outdated
} else { | ||
node.value = null; | ||
} | ||
if (node.computed !== undefined) node.computed = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that was a dodgy hack for some test failure because I had set computed
elsewhere by accident. I've fixed this now.
c367d80
to
d21cbfb
Compare
d21cbfb
to
6e7175d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a correct implementation of the grammar to me!
One surprising thing to me here, which I unfortunately missed in the earlier Private Fields PR, is that there's no single AST node for a private name. Instead, an Identifier is used inside of various different node types--previously, ClassPrivateProperty and PrivateName, now also ClassPrivateMethod. I would've expected that ClassPrivateProperty and ClassPrivateMethod would contain a PrivateName which contains an Identifier, instead of directly containing the Identifier.
I assume that the private methods plugin will be responsible for throwing early errors (such as duplicate method definitions); is that right?
I appreciate the tests here. Although I can think of some more tests for syntax edge cases, maybe those are best developed in test262.
src/parser/statement.js
Outdated
|
||
if (isPrivate) { | ||
// TODO: seems a bit inconsistent error throwing: in some cases we've already | ||
// eaten the #, but in other cases we haven't. How to solve? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could make expectPlugin
accept an optional pos
parameter (like unexpected
does) and pass node.start
to it.
src/parser/statement.js
Outdated
@@ -1009,21 +1019,43 @@ export default class StatementParser extends ExpressionParser { | |||
return; | |||
} | |||
|
|||
const isPrivate = this.match(tt.hash); | |||
if (isPrivate) { | |||
this.expectOnePlugin(["classPrivateProperties", "classPrivateMethods"]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of throwing here, you can wait until it is known if the node is a property or a method (to give a more specific error message).
method: N.ClassMethod, | ||
isGenerator: boolean, | ||
isAsync: boolean, | ||
isConstructor: boolean, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this ever be true
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. Not only that, it should be a syntax error; see below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed (not sure why it's not saying it's outdated).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm reading it right, this allows methods and fields named #constructor
. (That appears to have been behavior inherited from #609, which was right at the time but changed in tc39/proposal-class-fields@e7d009b.) It shouldn't, for fields or methods. Could you have parseClassPrivateName
throw in that case, and add a test or two?
Otherwise looks good to me.
src/parser/expression.js
Outdated
// TODO: if no plugin and is PrivateName, should error be handled here or elsewhere? | ||
if (prop.key.type !== "PrivateName") { | ||
// ClassPrivateProperty is never computed, so we don't assign in that case. | ||
prop.computed = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prop
here is a MemberExpression
? In that case, it should always be set to false
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the method signature says it's a Class[Private]Property
or Class[Private]Method
.
src/parser/statement.js
Outdated
if (isPrivate) { | ||
this.expectPlugin("classPrivateProperties", key.start); | ||
// Private property | ||
classBody.body.push(this.parseClassPrivateProperty(prop)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make a #pushClassPrivateProperty
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done -- should the two be merged or remain separate?
src/parser/statement.js
Outdated
method, | ||
false, | ||
false, | ||
isConstructor, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't be true
.
ce87518
to
5769ae5
Compare
I've done some updates to the PR -- will squash later once review done. While I'm at it, I'm just wondering whether the one test failure is important or not. This roughly equivalent non-private code just throws
Do we need it to check whether a semicolon should be there, and if so, how would I go about doing that? |
339b4fb
to
8645c96
Compare
@Qantas94Heavy, I don't think it's important that the failure says |
d5e0c38
to
8b5c2d5
Compare
src/parser/statement.js
Outdated
const isSimple = key.type === "Identifier"; | ||
|
||
// TODO: should these be merged into pushClassProperty? | ||
const handleClassProperty = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could just be inlined into the callsites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry, we need to update https://github.com/babel/babylon/pull/703/files#diff-aaeb808425fc8034ffaa66c89d53cf0aR1082. That's a PrivateName
now, right?
8b5c2d5
to
09c3f50
Compare
This commit adds parser support for the TC39 Stage 2 Private Methods proposal. This commit also changes "key" in ClassPrivateProperty from an Identifier to a PrivateName, as well as disallowing #constructor as a valid private field name.
This also removes a test from the Test262 whitelist that failed before the changes for private methods support and now passes.
These should be treated as regular methods and not special get/set/async behaviour.
09c3f50
to
62233fb
Compare
@jridgewell right, updated. |
This adds support for private methods, as described in https://github.com/littledan/proposal-private-methods.
I haven't made an issue for this proposal yet, but if we want it to be separate from #540, I can do so.
this.hasPlugin("plugin-name-here")
check so that your new plugin code only runs when that flag is turned on (not default behavior)I'll also need to submit changes to
babel-types
to match up with this PR.NOTE: There's still a little bit I'm not exactly sure about (tick means handled):
#
then throws the error, while in other parts it throws the error without "eating" the#
.expected ;
, but the error is still thrown in the same position in the test file. (edit: changed test to match new message)