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

classNameTDZError in computed prototype methods with class fields #10766

Open
huarse opened this issue Nov 26, 2019 · 5 comments
Open

classNameTDZError in computed prototype methods with class fields #10766

huarse opened this issue Nov 26, 2019 · 5 comments

Comments

@huarse
Copy link

@huarse huarse commented Nov 26, 2019

Bug Report

Input Code

class Foo {
  static nickname = 'Tom';

  ['HELLO']() {
    console.log('>>>>', Foo);
  }
}

Output code with babel

"use strict";

function _classNameTDZError(name) { throw new Error("Class \"" + name + "\" cannot be referenced in computed property keys."); }

class Foo {
  ['HELLO']() {
    console.log('>>>>', (_classNameTDZError("Foo"), Foo));
  }

}

Foo.nickname = 'Tom';

However, when I put the static nickname = 'Tom'; out of the class, like Foo.nickname = 'Tom'; or just remove this line, the output code is correct

Babel Configuration (babel.config.js, .babelrc, package.json#babel, cli command, .eslintrc)

  • Filename: .babelrc
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["Chrome >= 65", "Firefox >= 55", "Safari >= 11"]
      },
    }],
  ],
  "plugins": [
    ["@babel/plugin-proposal-class-properties", { "loose": true }],
  ]
}

Environment

- Babel version(s): 7.6.4
- Node/npm version: 12.13.0 / 6.12.0
- OS: OSX 10.14.6
- How you are using Babel: cli
@babel-bot

This comment has been minimized.

Copy link
Collaborator

@babel-bot babel-bot commented Nov 26, 2019

Hey @huarse! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack community that typically always has someone willing to help. You can sign-up here for an invite."

@JLHwung

This comment has been minimized.

Copy link
Contributor

@JLHwung JLHwung commented Nov 26, 2019

Here is a minimal reproducible example on REPL.

class C {
  bar;
  ['foo']() { C }
}

This ClassName TDZ was firstly introduced at #6855. It was supposed to throw reference error when class is referenced in the key of the computed path

class C {
  [C + 42]{};
}

However we should not throw when C is referenced in the body of the computed path. Here is the code that we should pay attention to

computedPath.traverse(classFieldDefinitionEvaluationTDZVisitor, {
classBinding: path.node.id && path.scope.getBinding(path.node.id.name),
file,
});

The computedPath is the whole computed property definition (i.e. [C + 42]{}), instead of traversing from the computedPath, we may consider to traverse from C + 42.

You may learn the structure of the AST node on Babel AST Browser

If you don't know how to clone Babel, follow these steps: (you need to have make and yarn available on your machine).

  1. Write a comment there to let other possible contributors know that you are working on this bug.
  2. Fork the repo
  3. Run git clone https://github.com/<YOUR_USERNAME>/babel.git && cd babel
  4. Run yarn && make bootstrap
  5. Wait
  6. Run make watch (or make build whenever you change a file)
  7. Add a test (only input.js; output.js will be automatically generated)
  8. Update the code!
  9. yarn jest babel-helper-create-class-features-plugins to run the tests
    • If some test outputs don't match but the new results are correct, you can delete the bad output.js files and run the tests again
  10. If it is working, run make test to run all the tests
  11. Run git push and open a PR!

@huarse Let me know if you want to give it a try. I will tweet this as good first issue tomorrow.

@nicolo-ribaudo

This comment has been minimized.

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Nov 26, 2019

Note that we also must make sure that it still throws when the class binding is used inside the initializer of a static class property.

@huarse

This comment has been minimized.

Copy link
Author

@huarse huarse commented Nov 26, 2019

@JLHwung I was trying to understood the code you mentioned , and then I found that even the feature Implement TDZ for ClassFieldDefinitionEvaluation has missed some case, eg.

class C {
  [C] = () => {};
}

this code would be add a _classNameTDZError wrapper, but,

class C {
  [C]() {}
}

this code would not be wrapped

@huarse huarse changed the title classNameTDZError in computed prototype methods with static class fields classNameTDZError in computed prototype methods with class fields Nov 26, 2019
@JLHwung

This comment has been minimized.

Copy link
Contributor

@JLHwung JLHwung commented Nov 26, 2019

@nicolo-ribaudo I don't think it should throw when the static field initializer references class binding. The initializer definition will be wrapped as function in ClassFieldDefinitionEvaluation, which will be executed later in DefineFields. For static fields, DefineFields run after classScopeEnvRec.InitializeBinding(className, F) in ClassDefinitionEvaluation, which means by the time when initializer is executed, the class binding has been initialized. For example,

class C { static P1 = 42; static P2 = (C.P1 === 42); }; C.P2;
// true
class D { static P2 = (D.P1 === 42); static P1 = 42; }; D.P2;
// false

Note that the property name is evaluated in ClassElementEvaluation, which is run before the class binding is initialized, therefore we should throw classNameTDZError.

@huarse The first example

class C {
  [C] = () => {};
}

demonstrates a computed class field and it should be processed by @babel/plugin-proposal-class-properties. On the other hand, @babel/plugin-proposal-class-properties should only take care of the computed class properties and leave computed method untouched.

The second example is computed class methods, and ideally it should be processed by @babel/transform-classes only, which means if user does not specified transform-classes, we should assume the browser has proper support on ClassNameTDZ on class methods so we don't add artificial check at all. On the other hand, if transform-classes is specified, the following code should include _classNameTDZError

class C {
  [C]() {}
}

Currently it does not throw so it is considered another bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.