Skip to content

Commit

Permalink
Handle logical assignment in super property transforms (#14164)
Browse files Browse the repository at this point in the history
  • Loading branch information
magic-akari committed Jan 17, 2022
1 parent e7c705a commit a6ca39c
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 22 deletions.
95 changes: 73 additions & 22 deletions packages/babel-traverse/src/path/conversion.ts
Expand Up @@ -11,6 +11,8 @@ import {
identifier,
isIdentifier,
jsxIdentifier,
logicalExpression,
LOGICAL_OPERATORS,
memberExpression,
metaProperty,
numericLiteral,
Expand Down Expand Up @@ -397,63 +399,96 @@ function hoistFunctionEnvironment(
return { thisBinding, fnPath };
}

function standardizeSuperProperty(superProp) {
type LogicalOp = Parameters<typeof logicalExpression>[0];
type BinaryOp = Parameters<typeof binaryExpression>[0];

function isLogicalOp(op: string): op is LogicalOp {
return LOGICAL_OPERATORS.includes(op);
}

function standardizeSuperProperty(superProp: NodePath<t.MemberExpression>) {
if (
superProp.parentPath.isAssignmentExpression() &&
superProp.parentPath.node.operator !== "="
) {
const assignmentPath = superProp.parentPath;

const op = assignmentPath.node.operator.slice(0, -1);
const op = assignmentPath.node.operator.slice(0, -1) as
| LogicalOp
| BinaryOp;

const value = assignmentPath.node.right;

assignmentPath.node.operator = "=";
const isLogicalAssignment = isLogicalOp(op);

if (superProp.node.computed) {
// from: super[foo] **= 4;
// to: super[tmp = foo] = super[tmp] ** 4;

// from: super[foo] ??= 4;
// to: super[tmp = foo] ?? super[tmp] = 4;

const tmp = superProp.scope.generateDeclaredUidIdentifier("tmp");

const object = superProp.node.object;
const property = superProp.node.property as t.Expression;

assignmentPath
.get("left")
.replaceWith(
memberExpression(
superProp.node.object,
assignmentExpression("=", tmp, superProp.node.property),
object,
assignmentExpression("=", tmp, property),
true /* computed */,
),
);

assignmentPath
.get("right")
.replaceWith(
binaryExpression(
op,
memberExpression(
superProp.node.object,
identifier(tmp.name),
true /* computed */,
),
rightExpression(
isLogicalAssignment ? "=" : op,
memberExpression(object, identifier(tmp.name), true /* computed */),
value,
),
);
} else {
// from: super.foo **= 4;
// to: super.foo = super.foo ** 4;

// from: super.foo ??= 4;
// to: super.foo ?? super.foo = 4;

const object = superProp.node.object;
const property = superProp.node.property as t.Identifier;

assignmentPath
.get("left")
.replaceWith(
memberExpression(superProp.node.object, superProp.node.property),
);
.replaceWith(memberExpression(object, property));

assignmentPath
.get("right")
.replaceWith(
binaryExpression(
op,
memberExpression(
superProp.node.object,
identifier(superProp.node.property.name),
),
rightExpression(
isLogicalAssignment ? "=" : op,
memberExpression(object, identifier(property.name)),
value,
),
);
}

if (isLogicalAssignment) {
assignmentPath.replaceWith(
logicalExpression(
op,
assignmentPath.node.left as t.Expression,
assignmentPath.node.right as t.Expression,
),
);
} else {
assignmentPath.node.operator = "=";
}

return [
assignmentPath.get("left"),
assignmentPath.get("right").get("left"),
Expand All @@ -473,7 +508,11 @@ function standardizeSuperProperty(superProp) {
memberExpression(
superProp.node.object,
computedKey
? assignmentExpression("=", computedKey, superProp.node.property)
? assignmentExpression(
"=",
computedKey,
superProp.node.property as t.Expression,
)
: superProp.node.property,
superProp.node.computed,
),
Expand Down Expand Up @@ -506,6 +545,18 @@ function standardizeSuperProperty(superProp) {
}

return [superProp];

function rightExpression(
op: BinaryOp | "=",
left: t.MemberExpression,
right: t.Expression,
) {
if (op === "=") {
return assignmentExpression("=", left, right);
} else {
return binaryExpression(op, left, right);
}
}
}

function hasSuperClass(thisEnvFn) {
Expand Down
150 changes: 150 additions & 0 deletions packages/babel-traverse/test/arrow-transform.js
Expand Up @@ -518,6 +518,156 @@ describe("arrow function conversion", () => {
);
});

it("should convert super.prop operator logical assign `??=`", () => {
assertConversion(
`
() => {
super.foo ??= 4;
};
super.foo ??= 4;
() => super.foo ??= 4;
`,
`
var _superprop_getFoo = () => super.foo,
_superprop_setFoo = _value => super.foo = _value;
(function () {
_superprop_getFoo() ?? _superprop_setFoo(4);
});
super.foo ??= 4;
() => super.foo ??= 4;
`,
);
});

it("should convert super.prop operator logical assign `&&=`", () => {
assertConversion(
`
() => {
super.foo &&= true;
};
super.foo &&= true;
() => super.foo &&= true;
`,
`
var _superprop_getFoo = () => super.foo,
_superprop_setFoo = _value => super.foo = _value;
(function () {
_superprop_getFoo() && _superprop_setFoo(true);
});
super.foo &&= true;
() => super.foo &&= true;
`,
);
});

it("should convert super.prop operator logical assign `||=`", () => {
assertConversion(
`
() => {
super.foo ||= true;
};
super.foo ||= true;
() => super.foo ||= true;
`,
`
var _superprop_getFoo = () => super.foo,
_superprop_setFoo = _value => super.foo = _value;
(function () {
_superprop_getFoo() || _superprop_setFoo(true);
});
super.foo ||= true;
() => super.foo ||= true;
`,
);
});

it("should convert super[prop] operator logical assign `??=`", () => {
assertConversion(
`
() => {
super[foo] ??= 4;
};
super[foo] ??= 4;
() => super[foo] ??= 4;
`,
`
var _superprop_get = _prop => super[_prop],
_superprop_set = (_prop2, _value) => super[_prop2] = _value;
(function () {
var _tmp;
_superprop_get(_tmp = foo) ?? _superprop_set(_tmp, 4);
});
super[foo] ??= 4;
() => super[foo] ??= 4;
`,
);
});

it("should convert super[prop] operator logical assign `&&=`", () => {
assertConversion(
`
() => {
super[foo] &&= true;
};
super[foo] &&= true;
() => super[foo] &&= true;
`,
`
var _superprop_get = _prop => super[_prop],
_superprop_set = (_prop2, _value) => super[_prop2] = _value;
(function () {
var _tmp;
_superprop_get(_tmp = foo) && _superprop_set(_tmp, true);
});
super[foo] &&= true;
() => super[foo] &&= true;
`,
);
});

it("should convert super[prop] operator logical assign `||=`", () => {
assertConversion(
`
() => {
super[foo] ||= true;
};
super[foo] ||= true;
() => super[foo] ||= true;
`,
`
var _superprop_get = _prop => super[_prop],
_superprop_set = (_prop2, _value) => super[_prop2] = _value;
(function () {
var _tmp;
_superprop_get(_tmp = foo) || _superprop_set(_tmp, true);
});
super[foo] ||= true;
() => super[foo] ||= true;
`,
);
});

it("should convert `++super.prop` prefix update", () => {
assertConversion(
`
Expand Down

0 comments on commit a6ca39c

Please sign in to comment.