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

add class.hasInstance proposal (Stage 1) support #13959

Closed
wants to merge 34 commits into from

Conversation

YuriTu
Copy link

@YuriTu YuriTu commented Nov 13, 2021

Q                       A
Fixed Issues? Darft https://github.com/tc39/proposal-class-brand-check
Patch: Bug Fix? No
Major: Breaking Change? No
Minor: New Feature? Yes
Tests Added + Pass? Yes
Documentation PR Link proposal-class-brand-check es-tree
Any Dependency Changes? No
License MIT

Add TC39 proposal: proposal-class-brand-check

usage:

class Range {

  equals(range) {
    if (!(class.hasInstance(range))) return false;
  }
}

after transform :

var _set = new WeakSet();

class Range {
  constructor() {
    _set.add(this);
  }

  equals(range) {
    if (!_set.has(range)) return false;
  }

}

@JLHwung
Copy link
Contributor

JLHwung commented Nov 14, 2021

Can you open an PR to ESTree on the AST shape of class.hasInstance? See also contribution guide about new Babel plugins.

Since it is an early draft, we will not review until you mark it as ready. Take your time and happy hacking. If you have any questions, please reach out at Slack.

@YuriTu YuriTu marked this pull request as ready for review March 14, 2022 08:39
Copy link
Contributor

@JLHwung JLHwung left a comment

Choose a reason for hiding this comment

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

Left some comments on the parsing part. Will do another review on the transformer later.

Update: we should also take a look at the statement parsing:

class.hasInstance(foo) as an ExpressionStatement should be valid.

packages/babel-parser/src/parser/expression.js Outdated Show resolved Hide resolved
packages/babel-parser/src/parser/expression.js Outdated Show resolved Hide resolved
packages/babel-parser/src/parser/expression.js Outdated Show resolved Hide resolved
@JLHwung JLHwung added PR: New Feature 🚀 A type of pull request used for our changelog categories Spec: Class Brand Check labels Mar 14, 2022
@@ -359,3 +359,9 @@ export function ModuleExpression(node: t.ModuleExpression) {
this.rightBrace();
}
}

export function ClassHasInstanceExpression(node: t.ClassHasInstanceExpression) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should ensure @babel/generator prints the code according to the AST:

Suggested change
export function ClassHasInstanceExpression(node: t.ClassHasInstanceExpression) {
export function ClassHasInstanceExpression(node: t.ClassHasInstanceExpression) {
this.word("class");
this.token(".");
this.word("hasInstance");
this.printInnerComments(node);

Please also add a test case to babel-generator/test/fixtures/class-brand-check:

class Range {
    equals(obj) {
        class.hasInstance(obj)
        class/* 1 */./* 2 */hasInstance/* 3 */(obj)
    }
}

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented Mar 24, 2022

We already have an AST node for meta properties:

export type MetaProperty = NodeBase & {
type: "MetaProperty",
meta: Identifier,
property: Identifier,
};

We should keep using it instead of creating a new one. class.hasInstance(obj) would then be represented as a CallExpression whose callee is a MetaProperty, similarly to how we represent super() and import() as CallExpressions.

You can take inspiration from where we call the parseMetaProperty function.

@JLHwung
Copy link
Contributor

JLHwung commented Mar 24, 2022

The current AST spec follows ESTree proposal in estree/estree#260. The ClassHasInstanceExpression is similar to the ImportExpression in ESTree.

The ClassHasInstanceExpression approach is more abstract than the MetaProperty approach. So the advantages of each of them follows the ones of AST v.s. CST.

Pros of ClassHasInstanceExpression:

  • The pattern is easier to detect than MetaProperty, which would require 4 checks: CallExpression, callee is MetaProperty, identifier is class and identifier is hasInstance
  • AST constructed from the @babel/types ClassHasInstanceExpression constructor is always correct.

Pros of MetaProperty:

  • Each non-punctuation token has corresponding syntax tree node so we don't need to deal with innerComments
  • The current call expression Babel plugins will automatically apply to class.hasInstance() as well
  • Better for parsing with recoverable errors: The syntax tree can represent more invalid AST, as long as accepted by the general CallExpression

If we are going with the MetaProperty approach, the pros of ClassHasInstanceExpression can be obtained from extra @babel/types helpers, but not vice versa.

tuqiang.yuri and others added 3 commits March 28, 2022 14:38
ensure @babel/generator prints the code according to the AST:

Co-authored-by: Huáng Jùnliàng <jlhwung@gmail.com>
@@ -229,8 +229,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
): T {
const type = isStatement ? "ClassDeclaration" : "ClassExpression";

this.next();
this.takeDecorators(node);
if (this.eat(tt._class)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

With this change we will incorrectly allow

class class A {}

because class is eaten twice.

I suggest we remove this.takeDecorators in parseClassOrClassHasInstanceExpression and remove this.next() above.

@@ -0,0 +1,975 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
Copy link
Contributor

Choose a reason for hiding this comment

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

The snapshot should be removed.


/* 2 */

/* 3 */
Copy link
Contributor

Choose a reason for hiding this comment

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

The output here is incorrect. It seems to me the comments are registered as innerComments of the BodyStatement, it should be attached as the innerComments of ClassHasInstanceExpression / MetaProperty.

@@ -132,6 +132,7 @@ export default (_: typeof toParseErrorCredentials) => ({
IncompatibleRegExpUVFlags: _(
"The 'u' and 'v' regular expression flags cannot be enabled at the same time.",
),
InvalidArguments: _("Invalid Arguments."),
Copy link
Contributor

Choose a reason for hiding this comment

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

The error message is a bit vague. We can provide more informative errors like we did for setters:

BadSetterArity: _("A 'set' accesor must have exactly one formal parameter."),
BadSetterRestParameter: _(
"A 'set' accesor function argument must not be a rest parameter.",
),

@babel-bot
Copy link
Collaborator

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/51691/

@nicolo-ribaudo
Copy link
Member

According to https://github.com/tc39/notes/blob/HEAD/meetings/2021-01/jan-27.md#class-brand-checks and tc39/proposal-class-brand-check#15, it's likely that the syntax will change because:

Given those likely changes, I think it would be better to wait for the proposal champion to present the updated proposal version to TC39 before merging this PR.

@YuriTu
Copy link
Author

YuriTu commented Apr 20, 2022

This is valuable advice. I will discuss the plugin's plans with the authors of the proposal.

@liuxingbaoyu
Copy link
Member

Thank you for your PR! Since this proposal is still in phase 1 and no longer seems to be active, I will close this PR.

@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 Feb 6, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 6, 2024
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 PR: New Feature 🚀 A type of pull request used for our changelog categories Spec: Class Brand Check
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants