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

fix: privateFieldsAsProperties global counter (#15389) #15393

Closed

Conversation

fwienber
Copy link
Contributor

@fwienber fwienber commented Feb 1, 2023

When babel-helpers are inlined, the global variable id was defined and initialized to zero in every generated file. This could lead to name clashes between #-private members in subclass and superclass, which leads to incorrect semantics.
The new solution uses Symbols to create a unique private slot that cannot name-clash. I assume that in old environments that don't support Symbol natively, (babel-)polyfills must be loaded.

For details and discussion of other solutions, see #15389.

Q                       A
Fixed Issues? Fixes #15389
Patch: Bug Fix? Yes
Major: Breaking Change?
Minor: New Feature?
Tests Added + Pass? No
Documentation PR Link babel/website#2721
Any Dependency Changes? No
License MIT

@babel-bot
Copy link
Collaborator

babel-bot commented Feb 1, 2023

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

@fwienber
Copy link
Contributor Author

fwienber commented Feb 1, 2023

Next, I would like to add tests, but there are some specialties involved to create a reproducer (see my codesandbox.io example):

  • enable inlining of babel-helpers. This could probably be achieved by setting "externalHelpers": false in options.json.
  • provide two input files, so that the helper code is inlined twice. Unfortunately, I don't see any way to achieve that with the current test setup.

@fwienber fwienber force-pushed the fix-15389-privateFieldsAsProperties branch from b49fc7f to a2656af Compare February 3, 2023 13:07
@fwienber

This comment was marked as outdated.

@liuxingbaoyu
Copy link
Member

liuxingbaoyu commented Feb 3, 2023

It might be a bit early to work on this PR now. It's best to let us agree on which method to choose in the end first, to be able to use the best method and avoid wasting some time.
Anyway, thanks for your contribution!

@fwienber
Copy link
Contributor Author

fwienber commented Feb 3, 2023

It might be a bit early to work on this PR now. It's best to let us agree on which method to choose in the end first, to be able to use the best method and avoid wasting some time. Anyway, thanks for your contribution!

This is why this PR is a "draft". I think a (draft) PR is the better place to discuss and try out concrete solutions, because it literally contains the code changes. I felt the issue was getting too long and into too much detail. But if you want, we can continue the discussion there.

@fwienber

This comment was marked as outdated.

When babel-helpers are inlined, classPrivateFieldLooseKey()
defines a global variable `id` and initialized it to zero
in every generated file. This can lead to name clashes
between #-private members in subclass and superclass,
which again leads to incorrect semantics.
In the new solution, instead of trying to produce a
globally unique private property name string, a Symbol
is created. The helper function classPrivateFieldLooseKey()
is no longer used, but kept for backwards-compatibility.
In environments where Symbol is not available (Internet
Explorer), a polyfill must be included.

For details and discussion of other solutions, see
babel#15389

For easier review, the test output updates follow in
a separate commit.
In all test output files (output.js and output.mjs),
replaced 'babelHelpers.classPrivateFieldLooseKey'
by       'Symbol'.

Tests are green.
@fwienber fwienber force-pushed the fix-15389-privateFieldsAsProperties branch from 367a6e4 to 34245f2 Compare February 9, 2023 18:07
@fwienber fwienber marked this pull request as ready for review February 9, 2023 18:36
@fwienber
Copy link
Contributor Author

fwienber commented Feb 9, 2023

All tests are green, ready for review.

@fwienber
Copy link
Contributor Author

@liuxingbaoyu what do you think? In the discussion in the corresponding issue, it seemed to me that everyone agrees that emitting Symbol (in contrast to using it directly in helper code) is fine.

liuxingbaoyu
liuxingbaoyu previously approved these changes Feb 14, 2023
Copy link
Member

@liuxingbaoyu liuxingbaoyu left a comment

Choose a reason for hiding this comment

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

My take on this PR is that we can try it out (see if anyone reports this after it's released, as it's technically a breaking change)

Even though there are two approvals now, if you want to review it! @nicolo-ribaudo

@@ -1,4 +1,4 @@
var _privateMethod = /*#__PURE__*/babelHelpers.classPrivateFieldLooseKey("privateMethod");
var _privateMethod = /*#__PURE__*/Symbol("privateMethod");
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm just about to remove the PURE comment for the new Symbol output and wondered whether it is needed for the existing standard solution, which generates new WeakMap() also with a PURE annotation. Shouldn't optimizer tools like Uglify know that creating a (built-in) WeakMap has no side-effects?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@liuxingbaoyu liuxingbaoyu added the PR: Bug Fix 🐛 A type of pull request used for our changelog categories label Feb 14, 2023
@nicolo-ribaudo
Copy link
Member

I'm on the fence with this. In theory it should be good, but technically it's a breaking change. Maybe we could introduce this under a new privateFieldsAsSymbols assumption, and leave the existing behavior intact. We can then deprecate the old assumption documenting the problem, and force people to switch in Babel 8.

We will release a new minor roughly at the end of the week, and we could inclue the new assumption in that version.

@liuxingbaoyu
Copy link
Member

Will the new assumptions be enabled by default? I'm afraid a lot of people haven't noticed it.

@nicolo-ribaudo
Copy link
Member

Well, not even the old privateFieldsAsProperties was enabled by default. A possible timeline is:

  • 7.21.0 - introduce the new assumption and document the problem in privateFieldsAsProperties
  • 7.22.0 - if @babel/plugin-transform-runtime is not enabled, warn that privateFieldsAsProperties is deprecated
  • 8.0.0 - remove privateFieldsAsProperties

@fwienber
Copy link
Contributor Author

Sounds good!
So how do we proceed with this? I could try to make the corresponding code changes and adapt the PR. Or is anyone else going to take over? I don't want to retreat, just want to avoid duplicate work.

@liuxingbaoyu
Copy link
Member

Feel free to do it!

@nicolo-ribaudo
Copy link
Member

Feel free to make the changes here! If you need an example of how to define a new assumption option, you can check 7e50ee2.

@liuxingbaoyu liuxingbaoyu added PR: New Feature 🚀 A type of pull request used for our changelog categories and removed PR: Bug Fix 🐛 A type of pull request used for our changelog categories labels Feb 16, 2023
@nicolo-ribaudo nicolo-ribaudo added this to the v7.21.0 milestone Feb 16, 2023
@nicolo-ribaudo
Copy link
Member

I can work on docs for this PR.

@fwienber
Copy link
Contributor Author

fwienber commented Feb 16, 2023

Well, not even the old privateFieldsAsProperties was enabled by default. A possible timeline is:

  • 7.21.0 - introduce the new assumption and document the problem in privateFieldsAsProperties
  • 7.22.0 - if @babel/plugin-transform-runtime is not enabled, warn that privateFieldsAsProperties is deprecated
  • 8.0.0 - remove privateFieldsAsProperties

What about the loose property of plugin-proposal-class-properties? If I get it right, it currently enables privateFieldsAsProperties. I am aware that "assumptions" are now the preferred way to do this, so would the behavior of using "loose" stay as-is in Babel 7 and change to enable privateFieldsAsSymbols instead in Babel 8?

@nicolo-ribaudo
Copy link
Member

Yes. Or we might even just remove the loose option in Babel 8.

@fwienber
Copy link
Contributor Author

More questions: If in 7.21.0/7.220, both privateFieldsAsProperties and privateFieldsAsSymbols are set, which one should win? Should there be a warning in that case that the other assumption was ineffective?

@fwienber
Copy link
Contributor Author

Since the new approach to introduce a dedicated assumption for using Symbol properties is quite different, I started a fresh branch and created a new PR #15435. I copied all relevant information (e.g. link to documentation PR), I hope I didn't miss anything. Let's continue there.

@fwienber fwienber closed this Feb 17, 2023
@liuxingbaoyu liuxingbaoyu removed this from the v7.21.0 milestone Feb 17, 2023
@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 May 20, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: assumptions 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 Fields
Projects
None yet
5 participants