Skip to content

Commit

Permalink
Fix update expression for exported bigints (#14341)
Browse files Browse the repository at this point in the history
  • Loading branch information
magic-akari committed Mar 10, 2022
1 parent 983f707 commit a7b1181
Show file tree
Hide file tree
Showing 20 changed files with 397 additions and 28 deletions.
Expand Up @@ -112,6 +112,7 @@ export default function rewriteLiveReferences(
programPath,
// NOTE(logan): The 'Array.from' calls are to make this code with in loose mode.
new Set([...Array.from(imported.keys()), ...Array.from(exported.keys())]),
false,
);

// Rewrite reads/writes from imports and exports to have the correct behavior.
Expand Down Expand Up @@ -290,6 +291,81 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
}
},

UpdateExpression(path) {
const {
scope,
seen,
imported,
exported,
requeueInParent,
buildImportReference,
} = this;

if (seen.has(path.node)) return;

seen.add(path.node);

const arg = path.get("argument");

// No change needed
if (arg.isMemberExpression()) return;

const update = path.node;

if (arg.isIdentifier()) {
const localName = arg.node.name;

// redeclared in this scope
if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
return;
}

const exportedNames = exported.get(localName);
const importData = imported.get(localName);

if (exportedNames?.length > 0 || importData) {
if (importData) {
path.replaceWith(
assignmentExpression(
update.operator[0] + "=",
buildImportReference(importData, arg.node),
buildImportThrow(localName),
),
);
} else if (update.prefix) {
// ++foo
// => exports.foo = ++foo
path.replaceWith(
buildBindingExportAssignmentExpression(
this.metadata,
exportedNames,
cloneNode(update),
),
);
} else {
// foo++
// => (ref = i++, exports.i = i, ref)
const ref = scope.generateDeclaredUidIdentifier(localName);

path.replaceWith(
sequenceExpression([
assignmentExpression("=", cloneNode(ref), cloneNode(update)),
buildBindingExportAssignmentExpression(
this.metadata,
exportedNames,
identifier(localName),
),
cloneNode(ref),
]),
);
}
}
}

requeueInParent(path);
path.skip();
},

AssignmentExpression: {
exit(path) {
const {
Expand Down
14 changes: 12 additions & 2 deletions packages/babel-helper-simple-access/src/index.ts
Expand Up @@ -11,18 +11,28 @@ import {
} from "@babel/types";
import type { NodePath } from "@babel/traverse";

export default function simplifyAccess(path: NodePath, bindingNames) {
export default function simplifyAccess(
path: NodePath,
bindingNames,
// TODO(Babel 8): Remove this
includeUpdateExpression: boolean = true,
) {
path.traverse(simpleAssignmentVisitor, {
scope: path.scope,
bindingNames,
seen: new WeakSet(),
includeUpdateExpression,
});
}

const simpleAssignmentVisitor = {
// TODO(Babel 8): Remove UpdateExpression
UpdateExpression: {
exit(path) {
const { scope, bindingNames } = this;
const { scope, bindingNames, includeUpdateExpression } = this;
if (!includeUpdateExpression) {
return;
}

const arg = path.get("argument");
if (!arg.isIdentifier()) return;
Expand Down
181 changes: 181 additions & 0 deletions packages/babel-helper-simple-access/test/index.js
@@ -0,0 +1,181 @@
import * as babel from "@babel/core";
import simplifyAccess from "../lib/index.js";

const plugin = (_api, options) => {
const { includeUpdateExpression, bindingNames } = options;

return {
visitor: {
Program(path) {
simplifyAccess.default(
path,
new Set(bindingNames),
includeUpdateExpression,
);
},
},
};
};

it("simplifyAccess with default config", function () {
const code = `
let a = foo++;
a = ++foo;
foo++;
++foo;
++a;
a++;
foo = a++;
foo = ++a;
let b = bar--;
b = --bar;
bar--;
--bar;
--b;
b--;
bar = b--;
bar = --b;
let c = baz += 1;
baz += 1;
c += 1;
function f() {
let foo = 1;
let a = foo++;
a = ++foo;
foo++;
++foo;
++a;
a++;
foo = a++;
foo = ++a;
}
`;

const output = babel.transformSync(code, {
configFile: false,
ast: false,
plugins: [[plugin, { bindingNames: ["foo", "bar", "baz"] }]],
}).code;

expect(output).toMatchInlineSnapshot(`
"var _foo, _bar;
let a = (_foo = +foo, foo = _foo + 1, _foo);
a = foo = +foo + 1;
foo = foo + 1;
foo = foo + 1;
++a;
a++;
foo = a++;
foo = ++a;
let b = (_bar = +bar, bar = _bar - 1, _bar);
b = bar = +bar - 1;
bar = bar - 1;
bar = bar - 1;
--b;
b--;
bar = b--;
bar = --b;
let c = baz = baz + 1;
baz = baz + 1;
c += 1;
function f() {
let foo = 1;
let a = foo++;
a = ++foo;
foo++;
++foo;
++a;
a++;
foo = a++;
foo = ++a;
}"
`);
});

it("simplifyAccess with includeUpdateExpression=false", function () {
const code = `
let a = foo++;
a = ++foo;
foo++;
++foo;
++a;
a++;
foo = a++;
foo = ++a;
let b = bar--;
b = --bar;
bar--;
--bar;
--b;
b--;
bar = b--;
bar = --b;
let c = baz += 1;
baz += 1;
c += 1;
function f() {
let foo = 1;
let a = foo++;
a = ++foo;
foo++;
++foo;
++a;
a++;
foo = a++;
foo = ++a;
}
`;

const output = babel.transformSync(code, {
configFile: false,
ast: false,
plugins: [
[
plugin,
{ includeUpdateExpression: false, bindingNames: ["foo", "bar", "baz"] },
],
],
}).code;

expect(output).toMatchInlineSnapshot(`
"let a = foo++;
a = ++foo;
foo++;
++foo;
++a;
a++;
foo = a++;
foo = ++a;
let b = bar--;
b = --bar;
bar--;
--bar;
--b;
b--;
bar = b--;
bar = --b;
let c = baz = baz + 1;
baz = baz + 1;
c += 1;
function f() {
let foo = 1;
let a = foo++;
a = ++foo;
foo++;
++foo;
++a;
a++;
foo = a++;
foo = ++a;
}"
`);
});
1 change: 1 addition & 0 deletions packages/babel-helper-simple-access/test/package.json
@@ -0,0 +1 @@
{ "type": "module" }
Expand Up @@ -5,10 +5,13 @@ define(["exports"], function (_exports) {
value: true
});
_exports.test = _exports.f = _exports.e = _exports.c = _exports.a = void 0;

var _test;

var test = 2;
_exports.test = test;
_exports.test = test = 5;
_exports.test = test = test + 1;
_test = test++, _exports.test = test, _test;

(function () {
var test = 2;
Expand Down
Expand Up @@ -3,10 +3,13 @@ define(["exports"], function (_exports) {

_exports.__esModule = true;
_exports.test = _exports.f = _exports.e = _exports.c = _exports.a = void 0;

var _test;

var test = 2;
_exports.test = test;
_exports.test = test = 5;
_exports.test = test = test + 1;
_test = test++, _exports.test = test, _test;

(function () {
var test = 2;
Expand Down
22 changes: 21 additions & 1 deletion packages/babel-plugin-transform-modules-commonjs/src/index.ts
Expand Up @@ -89,6 +89,26 @@ export default declare((api, options) => {
path.replaceWith(getAssertion(localName));
},

UpdateExpression(path) {
const arg = path.get("argument");
const localName = arg.node.name;
if (localName !== "module" && localName !== "exports") return;

const localBinding = path.scope.getBinding(localName);
const rootBinding = this.scope.getBinding(localName);

// redeclared in this scope
if (rootBinding !== localBinding) return;

path.replaceWith(
t.assignmentExpression(
path.node.operator[0] + "=",
arg.node,
getAssertion(localName),
),
);
},

AssignmentExpression(path) {
const left = path.get("left");
if (left.isIdentifier()) {
Expand Down Expand Up @@ -162,7 +182,7 @@ export default declare((api, options) => {
// These objects are specific to CommonJS and are not available in
// real ES6 implementations.
if (!allowCommonJSExports) {
simplifyAccess(path, new Set(["module", "exports"]));
simplifyAccess(path, new Set(["module", "exports"]), false);
path.traverse(moduleExportsVisitor, {
scope: path.scope,
});
Expand Down
Expand Up @@ -2,10 +2,13 @@

exports.__esModule = true;
exports.test = exports.f = exports.e = exports.c = exports.a = void 0;

var _test;

var test = 2;
exports.test = test;
exports.test = test = 5;
exports.test = test = test + 1;
_test = test++, exports.test = test, _test;

(function () {
var test = 2;
Expand Down

0 comments on commit a7b1181

Please sign in to comment.