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

refactor: remove inClassProperty parser state #10906

Merged
merged 1 commit into from Dec 24, 2019

Conversation

JLHwung
Copy link
Contributor

@JLHwung JLHwung commented Dec 22, 2019

Q                       A
Test is passed yes
License MIT

This PR refactors the new.target detection logic, so we can remove unnecessary inClassProperty parser state.

We are now going to prove that !this.scope.inNonArrowFunction && !this.state.inClassProperty is equivalent of !this.scope.inNonArrowFunction && !this.scope.inClass.

By definition of state.inClassProperty, scope.inClass: true implies that state.inClassProperty: true, therefore !state.inClassProperty implies !scope.inClass.

Now we asserts that under the condition of this.scope.inNonArrowFunction: false, !scope.inClass implies !state.inClassProperty.

If scope.inClass: false and scope.inClassProperty: true, we know that thisScope has been changed from SCOPE_CLASS to another scope type. Now by the definition of thisScope

(scope.flags & SCOPE_VAR || scope.flags & SCOPE_CLASS) &&
!(scope.flags & SCOPE_ARROW)

and the definition of SCOPE_VAR

SCOPE_VAR = SCOPE_PROGRAM | SCOPE_FUNCTION | SCOPE_TS_MODULE;

thisScope can only be one of SCOPE_PROGRAM, SCOPE_FUNCTION, SCOPE_TS_MODULE, since SCOPE_PROGRAM and SCOPE_TS_MODULE can not be nested inside class properties, thisScope must be SCOPE_FUNCTION and it also satisfies !SCOPE_ARROW, which means this.scope.inNonArrowFunction: true, but it contradicts the condition.

Q.E.D.

@JLHwung JLHwung added PR: Polish 💅 A type of pull request used for our changelog categories pkg: parser labels Dec 22, 2019
@mheiber
Copy link

mheiber commented Dec 22, 2019

I enjoyed the proof and think this is a really cool approach to making changes!

I'm asking the below because I'm interested in the role of verification in transpilers. It's not a criticism, and please feel free to disregard:

What is the advantage of this change? Is the change for readability or for performance?

If the change is for readability, I wonder if the explicitness of the extra parser state might help readability or maintainability, even if a proof shows that the state is redundant due to interactions between other features.

For example, after this change, it seems code maintainers must go through the proof again to see if the conclusion still holds when there is a language feature or Babel refactor that affects the scopes, function types, or class properties.

@JLHwung
Copy link
Contributor Author

JLHwung commented Dec 22, 2019

@mheiber Thank you for your insightful comments!

What is the advantage of this change? Is the change for readability or for performance?

The goal of this PR is to remove the usage of state.inClassProperty because its definition is vague. While the name looks straightforward, it is reset to false when parsing function declaration but, for example, not reset when parsing class method.

class C { prop = (function() { /* inClassProperty is false here */ })() }
class C { prop = class () { constructor() { /* inClassProperty is true here */ } } }

IMO inClassProperty should mean that there exists a ClassProperty node in the ancestors of current parsing node. It turns out when inClassProperty was used in the parser, it is more correct to check the scope flags instead of AST ancestry queries because various nodes can be nested in the class properties. (See #10771 for an example where inClassProperty is an inaccurate implementation)

For example, after this change, it seems code maintainers must go through the proof again to see if the conclusion still holds when there is a language feature or Babel refactor that affects the scopes, function types, or class properties.

The current SCOPE_CLASS flag includes class property initializers and class method/accessor body. We can offer some specialized helpers here

get inClassProperty() {
  thisScope satisfies (SCOPE_CLASS: true, SCOPE_FUNCTION: false)
}
get inClassMethod() {
   thisScope satisfies (SCOPE_CLASS: true, SCOPE_FUNCTION: true)
}

However, if any new language features depends on the ancestry query, we can surely bring back state.inClassProperty for a faithful implementation.

@mheiber
Copy link

mheiber commented Dec 24, 2019

Thanks @JLHwung, I can see how inClassProperty was confusing

@nicolo-ribaudo nicolo-ribaudo merged commit a18166d into babel:master Dec 24, 2019
@nicolo-ribaudo nicolo-ribaudo deleted the remove-state-in-class-property branch December 24, 2019 10:43
@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Mar 25, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 25, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated A closed issue/PR that is archived due to age. Recommended to make a new issue pkg: parser PR: Polish 💅 A type of pull request used for our changelog categories
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants