From 4ec26d458ef5d9c0f070ba8f29d19fca8171c386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Mon, 4 Apr 2022 10:36:36 -0400 Subject: [PATCH] fix: preserve function length when lowering params --- .../src/index.ts | 28 +++++++++++++++++-- .../function-params/input.js | 7 +++++ .../function-params/output.js | 26 +++++++++++++++++ .../options.json | 12 ++++++++ .../function-length/exec.js | 12 ++++++-- .../function-length/input.js | 7 +++++ .../function-length/output.js | 26 +++++++++++++++++ .../function-params/function-length/exec.js | 12 ++++++-- .../function-params/function-length/input.js | 7 +++++ .../function-params/function-length/output.js | 23 +++++++++++++++ 10 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/function-params/input.js create mode 100644 packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/function-params/output.js create mode 100644 packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/options.json create mode 100644 packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/input.js create mode 100644 packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/output.js create mode 100644 packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/input.js create mode 100644 packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/output.js diff --git a/packages/babel-plugin-proposal-destructuring-private/src/index.ts b/packages/babel-plugin-proposal-destructuring-private/src/index.ts index 21f6b8d0f25e..4779bdcef4af 100644 --- a/packages/babel-plugin-proposal-destructuring-private/src/index.ts +++ b/packages/babel-plugin-proposal-destructuring-private/src/index.ts @@ -15,6 +15,7 @@ import { types as t } from "@babel/core"; const { assignmentExpression, + assignmentPattern, cloneNode, expressionStatement, isExpressionStatement, @@ -42,19 +43,35 @@ export default declare(function ({ // (b, { #x: x } = I) => body // transforms to: // (b, p1) => { var { #x: x } = p1 === undefined ? I : p1; body; } - const index = path.node.params.findIndex(param => hasPrivateKeys(param)); - if (index === -1) return; + const firstPrivateIndex = path.node.params.findIndex(param => + hasPrivateKeys(param), + ); + if (firstPrivateIndex === -1) return; // wrap function body within IIFE if any param is shadowed convertFunctionParams(path, ignoreFunctionLength, () => false, false); // invariant: path.body is always a BlockStatement after `convertFunctionParams` const { node, scope } = path; const params = node.params; - const paramsAfterIndex = params.splice(index); + const firstAssignmentPatternIndex = ignoreFunctionLength + ? -1 + : params.findIndex(param => param.type === "AssignmentPattern"); + const paramsAfterIndex = params.splice(firstPrivateIndex); const { params: transformedParams, variableDeclaration } = buildVariableDeclarationFromParams(paramsAfterIndex, scope); path.get("body").unshiftContainer("body", variableDeclaration); params.push(...transformedParams); + // preserve function.length + // (b, p1) => {} + // transforms to + // (b, p1 = void 0) => {} + if (firstAssignmentPatternIndex >= firstPrivateIndex) { + params[firstAssignmentPatternIndex] = assignmentPattern( + // @ts-ignore The transformed assignment pattern must not be a RestElement + params[firstAssignmentPatternIndex], + scope.buildUndefinedNode(), + ); + } scope.crawl(); // the pattern will be handled by VariableDeclaration visitor. }, @@ -84,6 +101,11 @@ export default declare(function ({ // for (const { #x: x } of cls) body; // transforms to: // for (const ref of cls) { const { #x: x } = ref; body; } + // todo: the transform here assumes that any expression within + // the destructuring pattern (`{ #x: x }`), when evluated, do not interfere + // with the iterator of cls. Otherwise we have to pause the iterator and + // interleave the expressions. + // See also https://gist.github.com/nicolo-ribaudo/f8ac7916f89450f2ead77d99855b2098 const temp = scope.generateUidIdentifier("ref"); node.left = variableDeclaration(left.kind, [ variableDeclarator(temp, null), diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/function-params/input.js b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/function-params/input.js new file mode 100644 index 000000000000..ab2d2de13412 --- /dev/null +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/function-params/input.js @@ -0,0 +1,7 @@ +class C { + static #x; + static 0(...[...{0: { #x: x }}]) {} + static 1(a, b = 1, { #x: x }, ...c) {} + static 2(a, b, { #x: x } = C) {} + static 3(a, b, { #x: x }, c = 1) {} +} diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/function-params/output.js b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/function-params/output.js new file mode 100644 index 000000000000..9db270ef7691 --- /dev/null +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/function-params/output.js @@ -0,0 +1,26 @@ +class C { + static 0(..._p) { + var [..._p2] = _p, + x = babelHelpers.classStaticPrivateFieldSpecGet(_p2[0], C, _x); + } + + static 1(a, b = 1, _p3, ..._p4) { + var x = babelHelpers.classStaticPrivateFieldSpecGet(_p3, C, _x), + c = _p4; + } + + static 2(a, b, _p5) { + var x = babelHelpers.classStaticPrivateFieldSpecGet(_p5 === void 0 ? C : _p5, C, _x); + } + + static 3(a, b, _p6, _p7) { + var x = babelHelpers.classStaticPrivateFieldSpecGet(_p6, C, _x), + c = _p7 === void 0 ? 1 : _p7; + } + +} + +var _x = { + writable: true, + value: void 0 +}; diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/options.json b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/options.json new file mode 100644 index 000000000000..6101c1836f44 --- /dev/null +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/assumption-ignoreFunctionLength/options.json @@ -0,0 +1,12 @@ +{ + "assumptions": { + "ignoreFunctionLength": true + }, + "plugins": [ + "proposal-destructuring-private", + "proposal-class-static-block", + "proposal-class-properties", + "proposal-private-methods", + ["proposal-object-rest-spread", { "useBuiltIns": true }] + ] +} diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/exec.js b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/exec.js index 6ad25f261e99..2f89b81049d4 100644 --- a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/exec.js +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/exec.js @@ -1,5 +1,11 @@ class C { - #x; - static m(a, { #x: x }, ...b) {} + static #x; + static 0(...[...{0: { #x: x }}]) {} + static 1(a, b = 1, { #x: x }, ...c) {} + static 2(a, b, { #x: x } = C) {} + static 3(a, b, { #x: x }, c = 1) {} } -expect(C.m.length).toBe(2) +expect(C[0].length).toBe(0); +expect(C[1].length).toBe(1); +expect(C[2].length).toBe(2); +expect(C[3].length).toBe(3); diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/input.js b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/input.js new file mode 100644 index 000000000000..ab2d2de13412 --- /dev/null +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/input.js @@ -0,0 +1,7 @@ +class C { + static #x; + static 0(...[...{0: { #x: x }}]) {} + static 1(a, b = 1, { #x: x }, ...c) {} + static 2(a, b, { #x: x } = C) {} + static 3(a, b, { #x: x }, c = 1) {} +} diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/output.js b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/output.js new file mode 100644 index 000000000000..7d3cfbe5de7c --- /dev/null +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params--es2015/function-length/output.js @@ -0,0 +1,26 @@ +class C { + static 0(..._p) { + var [..._p2] = _p, + x = babelHelpers.classStaticPrivateFieldSpecGet(_p2[0], C, _x); + } + + static 1(a, b = 1, _p3, ..._p4) { + var x = babelHelpers.classStaticPrivateFieldSpecGet(_p3, C, _x), + c = _p4; + } + + static 2(a, b, _p5 = void 0) { + var x = babelHelpers.classStaticPrivateFieldSpecGet(_p5 === void 0 ? C : _p5, C, _x); + } + + static 3(a, b, _p6, _p7 = void 0) { + var x = babelHelpers.classStaticPrivateFieldSpecGet(_p6, C, _x), + c = _p7 === void 0 ? 1 : _p7; + } + +} + +var _x = { + writable: true, + value: void 0 +}; diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/exec.js b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/exec.js index 6ad25f261e99..2f89b81049d4 100644 --- a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/exec.js +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/exec.js @@ -1,5 +1,11 @@ class C { - #x; - static m(a, { #x: x }, ...b) {} + static #x; + static 0(...[...{0: { #x: x }}]) {} + static 1(a, b = 1, { #x: x }, ...c) {} + static 2(a, b, { #x: x } = C) {} + static 3(a, b, { #x: x }, c = 1) {} } -expect(C.m.length).toBe(2) +expect(C[0].length).toBe(0); +expect(C[1].length).toBe(1); +expect(C[2].length).toBe(2); +expect(C[3].length).toBe(3); diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/input.js b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/input.js new file mode 100644 index 000000000000..ab2d2de13412 --- /dev/null +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/input.js @@ -0,0 +1,7 @@ +class C { + static #x; + static 0(...[...{0: { #x: x }}]) {} + static 1(a, b = 1, { #x: x }, ...c) {} + static 2(a, b, { #x: x } = C) {} + static 3(a, b, { #x: x }, c = 1) {} +} diff --git a/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/output.js b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/output.js new file mode 100644 index 000000000000..8fdd49231735 --- /dev/null +++ b/packages/babel-plugin-proposal-destructuring-private/test/fixtures/function-params/function-length/output.js @@ -0,0 +1,23 @@ +class C { + static #x; + + static 0(..._p) { + var [..._p2] = _p, + x = _p2[0].#x; + } + + static 1(a, b = 1, _p3, ..._p4) { + var x = _p3.#x, + c = _p4; + } + + static 2(a, b, _p5 = void 0) { + var x = (_p5 === void 0 ? C : _p5).#x; + } + + static 3(a, b, _p6, _p7 = void 0) { + var x = _p6.#x, + c = _p7 === void 0 ? 1 : _p7; + } + +}