Skip to content

Commit

Permalink
fix: preserve function length when lowering params
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Apr 4, 2022
1 parent 0c388af commit 4ec26d4
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 9 deletions.
28 changes: 25 additions & 3 deletions packages/babel-plugin-proposal-destructuring-private/src/index.ts
Expand Up @@ -15,6 +15,7 @@ import { types as t } from "@babel/core";

const {
assignmentExpression,
assignmentPattern,
cloneNode,
expressionStatement,
isExpressionStatement,
Expand Down Expand Up @@ -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.
},
Expand Down Expand Up @@ -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),
Expand Down
@@ -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) {}
}
@@ -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
};
@@ -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 }]
]
}
@@ -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);
@@ -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) {}
}
@@ -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
};
@@ -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);
@@ -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) {}
}
@@ -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;
}

}

0 comments on commit 4ec26d4

Please sign in to comment.