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

Class Private Static Accessors #10217

Merged
merged 8 commits into from Sep 6, 2019

Conversation

tim-mc
Copy link
Contributor

@tim-mc tim-mc commented Jul 14, 2019

Q                       A
Fixed Issues? babel/proposals#56, closes #8052
Patch: Bug Fix? --
Major: Breaking Change? --
Minor: New Feature? Add class private static accessor support
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes? No
License MIT

cc @robpalme @nicolo-ribaudo @littledan

@tim-mc
Copy link
Contributor Author

tim-mc commented Jul 14, 2019

Still a WIP. Spec support has been added. Working on loose now.

@babel-bot
Copy link
Collaborator

babel-bot commented Jul 14, 2019

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

@tim-mc tim-mc marked this pull request as ready for review July 15, 2019 15:52
@tim-mc
Copy link
Contributor Author

tim-mc commented Jul 15, 2019

Loose support has been added

@nicolo-ribaudo nicolo-ribaudo self-requested a review July 15, 2019 16:02
@tim-mc tim-mc force-pushed the private-static-accessors branch 2 times, most recently from a8def51 to 60b2fa9 Compare July 17, 2019 23:44
@@ -300,8 +303,47 @@ function buildPrivateInstanceFieldInitSpec(ref, prop, privateNamesMap) {
}

function buildPrivateStaticFieldInitSpec(prop, privateNamesMap) {
const { id } = privateNamesMap.get(prop.node.key.id.name);
const privateName = privateNamesMap.get(prop.node.key.id.name);
const { id, getId, setId, initAdded } = privateName;
const value = prop.node.value || prop.scope.buildUndefinedNode();
Copy link
Contributor

Choose a reason for hiding this comment

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

I suggest move this line immediately before where value is used so we can early return for non-accessor class private methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

const value = prop.node.value || prop.scope.buildUndefinedNode();
if (!prop.isProperty() && (initAdded || !(getId || setId))) return;
Copy link
Member

Choose a reason for hiding this comment

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

When would it not be a property?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It wouldn't be a property if it's a static private accessor.

Copy link
Member

Choose a reason for hiding this comment

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

Nit: could you extract getId || setId to an isAccessor boolean variable?
I find it hard to read all these nested boolean expressions.

// enumerable is false by default
// writable is false by default
get: ${getId.name},
set: ${setId.name}
Copy link
Member

Choose a reason for hiding this comment

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

Ditto reuse.

Copy link
Contributor Author

@tim-mc tim-mc Jul 22, 2019

Choose a reason for hiding this comment

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

That works in this case, but setting null as a getter/setter here is a TypeError.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, yah. We should be able to use undefined in all cases instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, updated.

@@ -615,14 +697,14 @@ export function buildFieldsInitNodes(
case isStatic && isPrivate && isMethod && !loose:
needsClassRef = true;
staticNodes.push(
buildPrivateStaticFieldInitSpec(prop, privateNamesMap),
);
staticNodes.unshift(
Copy link
Member

Choose a reason for hiding this comment

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

Why unshift?

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 use unshift so that the declaration of the getter and/or setter occurs before the usage of the accessor(s) (e.g., when they're defined as the values on an object). Here's what a test failure looks like if that's changed to push:

    babel/packages/babel-plugin-proposal-private-methods/test/fixtures/static-accessors/basic/exec.js: expect(received).toEqual(expected) // deep equality

    Expected: "top secret string"
    Received: undefined
       1 | class Cl {
       2 |   static getValue() {
       3 |     return babelHelpers.classStaticPrivateFieldSpecGet(Cl, Cl, _privateStaticFieldValue);
       4 |   }
       5 |
       6 |   static setValue() {
       7 |     babelHelpers.classStaticPrivateFieldSpecSet(Cl, Cl, _privateStaticFieldValue, "dank");
       8 |   }
       9 |
      10 | }
      11 |
      12 | var _PRIVATE_STATIC_FIELD = {
      13 |   writable: true,
      14 |   value: "top secret string"
      15 | };
      16 | var _privateStaticFieldValue = {
      17 |   get: _get_privateStaticFieldValue,
      18 |   set: _set_privateStaticFieldValue
      19 | };
      20 |
      21 | var _get_privateStaticFieldValue = function () {
      22 |   return babelHelpers.classStaticPrivateFieldSpecGet(Cl, Cl, _PRIVATE_STATIC_FIELD);
      23 | };
      24 |
      25 | var _set_privateStaticFieldValue = function (newValue) {
      26 |   babelHelpers.classStaticPrivateFieldSpecSet(Cl, Cl, _PRIVATE_STATIC_FIELD, `Updated: ${newValue}`);
      27 | };
      28 |
      29 | expect(Cl.getValue()).toEqual("top secret string");
      30 | Cl.setValue();
      31 | expect(Cl.getValue()).toEqual("Updated: dank");

Copy link
Member

Choose a reason for hiding this comment

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

Can we change the get/set variables to function declarations instead? If not, this is still fine.

const value = prop.node.value || prop.scope.buildUndefinedNode();
if (!prop.isProperty() && (initAdded || !(getId || setId))) return;
Copy link
Member

Choose a reason for hiding this comment

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

Nit: could you extract getId || setId to an isAccessor boolean variable?
I find it hard to read all these nested boolean expressions.

@tim-mc tim-mc force-pushed the private-static-accessors branch 2 times, most recently from 1bcccbd to 5ec8290 Compare July 29, 2019 15:51
@nicolo-ribaudo nicolo-ribaudo added PR: New Feature 🚀 A type of pull request used for our changelog categories Spec: Private Methods labels Aug 1, 2019
@nicolo-ribaudo nicolo-ribaudo added this to the v7.6.0 milestone Aug 1, 2019
@nicolo-ribaudo nicolo-ribaudo added the PR: Ready to be Merged A pull request with already two approvals, but waiting for the next minor release label Aug 1, 2019
@nicolo-ribaudo nicolo-ribaudo merged commit 3e4889d into babel:master Sep 6, 2019
@lock lock bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Dec 6, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Dec 6, 2019
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 PR: Ready to be Merged A pull request with already two approvals, but waiting for the next minor release Spec: Private Methods
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Static Class Features: Stage 3
5 participants