From c8e4224f4cbb3982e3c918135b4c78270d993199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 26 Jul 2021 23:31:45 +0200 Subject: [PATCH] Extract computed keys from the class closure (#13600) --- .../output.js | 6 ++-- .../nested-class-computed/output.js | 8 ++--- .../output.js | 6 ++-- .../private/nested-class-computed/output.js | 8 ++--- .../fixtures/public-loose/computed/output.js | 10 +++--- .../test/fixtures/public/computed/output.js | 10 +++--- .../decorator-interop/output.js | 6 ++-- .../src/transformClass.ts | 29 +++++++++++++++- .../get-set/change-computed-key/output.js | 10 +++--- .../spec/computed-key-class-tdz/exec.js | 5 +++ .../spec/computed-key-class-tdz/options.json | 4 +++ .../spec/computed-keys-call-order/exec.js | 11 ++++++ .../spec/computed-keys-yield-await/input.js | 9 +++++ .../spec/computed-keys-yield-await/output.js | 34 +++++++++++++++++++ .../fixtures/spec/computed-methods/output.js | 8 ++--- .../output.js | 14 ++++---- .../nested-class-super-call-in-key/output.js | 8 ++--- .../output.js | 6 ++-- .../spec/relaxed-method-coercion/output.js | 8 ++--- 19 files changed, 142 insertions(+), 58 deletions(-) create mode 100644 packages/babel-plugin-transform-classes/test/fixtures/spec/computed-key-class-tdz/exec.js create mode 100644 packages/babel-plugin-transform-classes/test/fixtures/spec/computed-key-class-tdz/options.json create mode 100644 packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-call-order/exec.js create mode 100644 packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-yield-await/input.js create mode 100644 packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-yield-await/output.js diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/nested-class-computed-redeclared/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/nested-class-computed-redeclared/output.js index f3dbcb3ae308..d4b862f38e4f 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/nested-class-computed-redeclared/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/nested-class-computed-redeclared/output.js @@ -20,7 +20,7 @@ var Foo = /*#__PURE__*/function () { _babelHelpers$classPr = babelHelpers.classPrivateFieldLooseBase(this, _foo2)[_foo2]; - var Nested = /*#__PURE__*/function () { + var Nested = /*#__PURE__*/function (_babelHelpers$classPr2) { function Nested() { babelHelpers.classCallCheck(this, Nested); Object.defineProperty(this, _foo2, { @@ -30,11 +30,11 @@ var Foo = /*#__PURE__*/function () { } babelHelpers.createClass(Nested, [{ - key: _babelHelpers$classPr, + key: _babelHelpers$classPr2, value: function () {} }]); return Nested; - }(); + }(_babelHelpers$classPr); babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo]; } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/nested-class-computed/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/nested-class-computed/output.js index 0b8c486d9d02..3557d34b5740 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/nested-class-computed/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/nested-class-computed/output.js @@ -14,19 +14,17 @@ var Foo = /*#__PURE__*/function () { babelHelpers.createClass(Foo, [{ key: "test", value: function test() { - var _this = this; - - var Nested = /*#__PURE__*/function () { + var Nested = /*#__PURE__*/function (_babelHelpers$classPr) { function Nested() { babelHelpers.classCallCheck(this, Nested); } babelHelpers.createClass(Nested, [{ - key: babelHelpers.classPrivateFieldLooseBase(_this, _foo)[_foo], + key: _babelHelpers$classPr, value: function () {} }]); return Nested; - }(); + }(babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo]); babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo]; } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/nested-class-computed-redeclared/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/nested-class-computed-redeclared/output.js index 085b255c262d..1dd4c23b4a31 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/nested-class-computed-redeclared/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/nested-class-computed-redeclared/output.js @@ -21,7 +21,7 @@ var Foo = /*#__PURE__*/function () { _babelHelpers$classPr = babelHelpers.classPrivateFieldGet(this, _foo2); - var Nested = /*#__PURE__*/function () { + var Nested = /*#__PURE__*/function (_babelHelpers$classPr2) { function Nested() { babelHelpers.classCallCheck(this, Nested); @@ -32,11 +32,11 @@ var Foo = /*#__PURE__*/function () { } babelHelpers.createClass(Nested, [{ - key: _babelHelpers$classPr, + key: _babelHelpers$classPr2, value: function () {} }]); return Nested; - }(); + }(_babelHelpers$classPr); babelHelpers.classPrivateFieldGet(this, _foo); } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/nested-class-computed/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/nested-class-computed/output.js index 102e9223946e..e5d21a94663d 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/nested-class-computed/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/nested-class-computed/output.js @@ -15,19 +15,17 @@ var Foo = /*#__PURE__*/function () { babelHelpers.createClass(Foo, [{ key: "test", value: function test() { - var _this = this; - - var Nested = /*#__PURE__*/function () { + var Nested = /*#__PURE__*/function (_babelHelpers$classPr) { function Nested() { babelHelpers.classCallCheck(this, Nested); } babelHelpers.createClass(Nested, [{ - key: babelHelpers.classPrivateFieldGet(_this, _foo), + key: _babelHelpers$classPr, value: function () {} }]); return Nested; - }(); + }(babelHelpers.classPrivateFieldGet(this, _foo)); babelHelpers.classPrivateFieldGet(this, _foo); } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/computed/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/computed/output.js index 568c7b0116b1..0c430c431ec4 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/computed/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/computed/output.js @@ -15,7 +15,7 @@ _ref3 = /regex/; _baz = baz; _ref4 = `template${expression}`; -var MyClass = /*#__PURE__*/function () { +var MyClass = /*#__PURE__*/function (_computed3, _computed4, _ref5) { "use strict"; function MyClass() { @@ -36,20 +36,20 @@ var MyClass = /*#__PURE__*/function () { get: function () {}, set: function (value) {} }, { - key: _computed, + key: _computed3, get: function () {} }, { - key: _computed2, + key: _computed4, set: function (value) {} }, { - key: _ref2, + key: _ref5, value: function () {} }], [{ key: "10", value: function _() {} }]); return MyClass; -}(); +}(_computed, _computed2, _ref2); MyClass[_one] = "test"; MyClass[2 * 4 + 7] = "247"; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed/output.js index aeca991c9373..8c78f5dc5401 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed/output.js @@ -15,7 +15,7 @@ _ref3 = /regex/; _baz = baz; _ref4 = `template${expression}`; -var MyClass = /*#__PURE__*/function () { +var MyClass = /*#__PURE__*/function (_computed3, _computed4, _ref5) { "use strict"; function MyClass() { @@ -36,20 +36,20 @@ var MyClass = /*#__PURE__*/function () { get: function () {}, set: function (value) {} }, { - key: _computed, + key: _computed3, get: function () {} }, { - key: _computed2, + key: _computed4, set: function (value) {} }, { - key: _ref2, + key: _ref5, value: function () {} }], [{ key: "10", value: function _() {} }]); return MyClass; -}(); +}(_computed, _computed2, _ref2); babelHelpers.defineProperty(MyClass, _one, "test"); babelHelpers.defineProperty(MyClass, 2 * 4 + 7, "247"); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/decorator-interop/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/decorator-interop/output.js index 63c65192b695..c424973adebb 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/decorator-interop/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/decorator-interop/output.js @@ -4,7 +4,7 @@ var _class, _descriptor; function dec() {} -let A = (_class = (_Symbol$search = Symbol.search, /*#__PURE__*/function () { +let A = (_class = (_Symbol$search = Symbol.search, /*#__PURE__*/function (_Symbol$search2) { "use strict"; function A() { @@ -13,11 +13,11 @@ let A = (_class = (_Symbol$search = Symbol.search, /*#__PURE__*/function () { } babelHelpers.createClass(A, [{ - key: _Symbol$search, + key: _Symbol$search2, value: function () {} }]); return A; -}()), (_descriptor = babelHelpers.applyDecoratedDescriptor(_class.prototype, "a", [dec], { +}(_Symbol$search)), (_descriptor = babelHelpers.applyDecoratedDescriptor(_class.prototype, "a", [dec], { configurable: true, enumerable: true, writable: true, diff --git a/packages/babel-plugin-transform-classes/src/transformClass.ts b/packages/babel-plugin-transform-classes/src/transformClass.ts index fcaaac5445bc..4f4a8b6b332e 100644 --- a/packages/babel-plugin-transform-classes/src/transformClass.ts +++ b/packages/babel-plugin-transform-classes/src/transformClass.ts @@ -62,6 +62,8 @@ export default function transformClass( protoAlias: null, isLoose: false, + dynamicKeys: new Map(), + methods: { // 'list' is in the same order as the elements appear in the class body. // if there aren't computed keys, we can safely reorder class elements @@ -600,8 +602,26 @@ export default function transformClass( ); } + function extractDynamicKeys() { + const { dynamicKeys, node, scope } = classState as { + dynamicKeys: Map; + node: t.Class; + scope: NodePath["scope"]; + }; + + for (const elem of node.body.body) { + if (!t.isClassMethod(elem) || !elem.computed) continue; + if (scope.isPure(elem.key, /* constatns only*/ true)) continue; + + const id = scope.generateUidIdentifierBasedOnNode(elem.key); + dynamicKeys.set(id.name, elem.key); + + elem.key = id; + } + } + function setupClosureParamsArgs() { - const { superName } = classState; + const { superName, dynamicKeys } = classState; const closureParams = []; const closureArgs = []; @@ -623,6 +643,11 @@ export default function transformClass( setState({ superName: t.cloneNode(param) }); } + for (const [name, value] of dynamicKeys) { + closureParams.push(t.identifier(name)); + closureArgs.push(value); + } + return { closureParams, closureArgs }; } @@ -668,6 +693,8 @@ export default function transformClass( construct: buildConstructor(classRef, constructorBody, node), }); + extractDynamicKeys(); + let { body } = classState; const { closureParams, closureArgs } = setupClosureParamsArgs(); diff --git a/packages/babel-plugin-transform-classes/test/fixtures/get-set/change-computed-key/output.js b/packages/babel-plugin-transform-classes/test/fixtures/get-set/change-computed-key/output.js index 8e5ae962c606..a08a170daf74 100644 --- a/packages/babel-plugin-transform-classes/test/fixtures/get-set/change-computed-key/output.js +++ b/packages/babel-plugin-transform-classes/test/fixtures/get-set/change-computed-key/output.js @@ -1,4 +1,4 @@ -let A = /*#__PURE__*/function () { +let A = /*#__PURE__*/function (_x, _ref, _x2) { "use strict"; function A() { @@ -6,14 +6,14 @@ let A = /*#__PURE__*/function () { } babelHelpers.createClass(A, [{ - key: x, + key: _x, get: function () {} }, { - key: (x = 2, 3), + key: _ref, value: function () {} }, { - key: x, + key: _x2, set: function (_) {} }]); return A; -}(); +}(x, (x = 2, 3), x); diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-key-class-tdz/exec.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-key-class-tdz/exec.js new file mode 100644 index 000000000000..afb11a7419e7 --- /dev/null +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-key-class-tdz/exec.js @@ -0,0 +1,5 @@ +expect(() => { + class A { + [A.name]() {} + } +}).toThrow(ReferenceError); diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-key-class-tdz/options.json b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-key-class-tdz/options.json new file mode 100644 index 000000000000..2e14e9fc86cb --- /dev/null +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-key-class-tdz/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["transform-classes"], + "minNodeVersion": "10.0.0" +} diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-call-order/exec.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-call-order/exec.js new file mode 100644 index 000000000000..f1e7372457c8 --- /dev/null +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-call-order/exec.js @@ -0,0 +1,11 @@ +var log = []; + +class A { + [log.push(1)]() {} + static [log.push(2)]() {} + [log.push(3)]() {} + static [log.push(4)]() {} +} + +expect(log).toEqual([1, 2, 3, 4]); + diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-yield-await/input.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-yield-await/input.js new file mode 100644 index 000000000000..c7995163ed03 --- /dev/null +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-yield-await/input.js @@ -0,0 +1,9 @@ +async function* fn() { + class A { + [yield 1]() {} + } + + class B extends A { + [await 1]() {} + } +} diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-yield-await/output.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-yield-await/output.js new file mode 100644 index 000000000000..aec80d976bac --- /dev/null +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-keys-yield-await/output.js @@ -0,0 +1,34 @@ +async function* fn() { + var A = /*#__PURE__*/function (_yield$) { + "use strict"; + + function A() { + babelHelpers.classCallCheck(this, A); + } + + babelHelpers.createClass(A, [{ + key: _yield$, + value: function value() {} + }]); + return A; + }(yield 1); + + var B = /*#__PURE__*/function (_A, _await$) { + "use strict"; + + babelHelpers.inherits(B, _A); + + var _super = babelHelpers.createSuper(B); + + function B() { + babelHelpers.classCallCheck(this, B); + return _super.apply(this, arguments); + } + + babelHelpers.createClass(B, [{ + key: _await$, + value: function value() {} + }]); + return B; + }(A, await 1); +} diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-methods/output.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-methods/output.js index ef64056212c7..bc107f1a1b90 100644 --- a/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-methods/output.js +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/computed-methods/output.js @@ -1,4 +1,4 @@ -var Foo = /*#__PURE__*/function () { +var Foo = /*#__PURE__*/function (_bar, _ref) { "use strict"; function Foo() { @@ -11,11 +11,11 @@ var Foo = /*#__PURE__*/function () { "second"; } }, { - key: bar, + key: _bar, value: function value() {} }, { - key: bar + "foo", + key: _ref, value: function value() {} }]); return Foo; -}(); +}(bar, bar + "foo"); diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/name-collisions-with-class-ref-in-computed-key/output.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/name-collisions-with-class-ref-in-computed-key/output.js index 71b59391fdeb..84bdf2ee075c 100644 --- a/packages/babel-plugin-transform-classes/test/fixtures/spec/name-collisions-with-class-ref-in-computed-key/output.js +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/name-collisions-with-class-ref-in-computed-key/output.js @@ -11,21 +11,21 @@ var Foo = /*#__PURE__*/function (_Bar) { babelHelpers.classCallCheck(this, Foo); _this = _super.call(this); - var X = /*#__PURE__*/function () { + var X = /*#__PURE__*/function (_ref) { function X() { babelHelpers.classCallCheck(this, X); } babelHelpers.createClass(X, [{ - key: (() => { - var _Foo; - - babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(Foo.prototype)), "method", _thisSuper).call(_thisSuper); - })(), + key: _ref, value: function value() {} }]); return X; - }(); + }((() => { + var _Foo; + + babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(Foo.prototype)), "method", _thisSuper).call(_thisSuper); + })()); return _this; } diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/nested-class-super-call-in-key/output.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/nested-class-super-call-in-key/output.js index fe4902958d94..b1e76457cf36 100644 --- a/packages/babel-plugin-transform-classes/test/fixtures/spec/nested-class-super-call-in-key/output.js +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/nested-class-super-call-in-key/output.js @@ -16,25 +16,23 @@ var Outer = /*#__PURE__*/function (_Hello) { var _super = babelHelpers.createSuper(Outer); function Outer() { - var _this2 = this; - var _this; babelHelpers.classCallCheck(this, Outer); - var Inner = /*#__PURE__*/function () { + var Inner = /*#__PURE__*/function (_this2) { function Inner() { babelHelpers.classCallCheck(this, Inner); } babelHelpers.createClass(Inner, [{ - key: _this = _super.call(_this2), + key: _this2, value: function value() { return 'hello'; } }]); return Inner; - }(); + }(_this = _super.call(this)); return babelHelpers.possibleConstructorReturn(_this, new Inner()); } diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/nested-class-super-property-in-key/output.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/nested-class-super-property-in-key/output.js index 9b3145d21416..bf1411294fe7 100644 --- a/packages/babel-plugin-transform-classes/test/fixtures/spec/nested-class-super-property-in-key/output.js +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/nested-class-super-property-in-key/output.js @@ -25,19 +25,19 @@ var Outer = /*#__PURE__*/function (_Hello) { babelHelpers.classCallCheck(this, Outer); _this = _super.call(this); - var Inner = /*#__PURE__*/function () { + var Inner = /*#__PURE__*/function (_babelHelpers$get$cal) { function Inner() { babelHelpers.classCallCheck(this, Inner); } babelHelpers.createClass(Inner, [{ - key: babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(Outer.prototype)), "toString", _thisSuper).call(_thisSuper), + key: _babelHelpers$get$cal, value: function value() { return 'hello'; } }]); return Inner; - }(); + }(babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(Outer.prototype)), "toString", _thisSuper).call(_thisSuper)); return babelHelpers.possibleConstructorReturn(_this, new Inner()); } diff --git a/packages/babel-plugin-transform-classes/test/fixtures/spec/relaxed-method-coercion/output.js b/packages/babel-plugin-transform-classes/test/fixtures/spec/relaxed-method-coercion/output.js index 5d9a2b9a2e46..6379fd21f1c1 100644 --- a/packages/babel-plugin-transform-classes/test/fixtures/spec/relaxed-method-coercion/output.js +++ b/packages/babel-plugin-transform-classes/test/fixtures/spec/relaxed-method-coercion/output.js @@ -1,5 +1,5 @@ // #1649 -var Foo = /*#__PURE__*/function () { +var Foo = /*#__PURE__*/function (_Symbol, _Symbol2) { "use strict"; function Foo() { @@ -7,11 +7,11 @@ var Foo = /*#__PURE__*/function () { } babelHelpers.createClass(Foo, [{ - key: Symbol(), + key: _Symbol, value: function value() {} }, { - key: Symbol(), + key: _Symbol2, value: function value() {} }]); return Foo; -}(); +}(Symbol(), Symbol());