Skip to content

Commit a7b1181

Browse files
authored
Fix update expression for exported bigints (#14341)
1 parent 983f707 commit a7b1181

20 files changed

Lines changed: 397 additions & 28 deletions

File tree

packages/babel-helper-module-transforms/src/rewrite-live-references.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export default function rewriteLiveReferences(
112112
programPath,
113113
// NOTE(logan): The 'Array.from' calls are to make this code with in loose mode.
114114
new Set([...Array.from(imported.keys()), ...Array.from(exported.keys())]),
115+
false,
115116
);
116117

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

294+
UpdateExpression(path) {
295+
const {
296+
scope,
297+
seen,
298+
imported,
299+
exported,
300+
requeueInParent,
301+
buildImportReference,
302+
} = this;
303+
304+
if (seen.has(path.node)) return;
305+
306+
seen.add(path.node);
307+
308+
const arg = path.get("argument");
309+
310+
// No change needed
311+
if (arg.isMemberExpression()) return;
312+
313+
const update = path.node;
314+
315+
if (arg.isIdentifier()) {
316+
const localName = arg.node.name;
317+
318+
// redeclared in this scope
319+
if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
320+
return;
321+
}
322+
323+
const exportedNames = exported.get(localName);
324+
const importData = imported.get(localName);
325+
326+
if (exportedNames?.length > 0 || importData) {
327+
if (importData) {
328+
path.replaceWith(
329+
assignmentExpression(
330+
update.operator[0] + "=",
331+
buildImportReference(importData, arg.node),
332+
buildImportThrow(localName),
333+
),
334+
);
335+
} else if (update.prefix) {
336+
// ++foo
337+
// => exports.foo = ++foo
338+
path.replaceWith(
339+
buildBindingExportAssignmentExpression(
340+
this.metadata,
341+
exportedNames,
342+
cloneNode(update),
343+
),
344+
);
345+
} else {
346+
// foo++
347+
// => (ref = i++, exports.i = i, ref)
348+
const ref = scope.generateDeclaredUidIdentifier(localName);
349+
350+
path.replaceWith(
351+
sequenceExpression([
352+
assignmentExpression("=", cloneNode(ref), cloneNode(update)),
353+
buildBindingExportAssignmentExpression(
354+
this.metadata,
355+
exportedNames,
356+
identifier(localName),
357+
),
358+
cloneNode(ref),
359+
]),
360+
);
361+
}
362+
}
363+
}
364+
365+
requeueInParent(path);
366+
path.skip();
367+
},
368+
293369
AssignmentExpression: {
294370
exit(path) {
295371
const {

packages/babel-helper-simple-access/src/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,28 @@ import {
1111
} from "@babel/types";
1212
import type { NodePath } from "@babel/traverse";
1313

14-
export default function simplifyAccess(path: NodePath, bindingNames) {
14+
export default function simplifyAccess(
15+
path: NodePath,
16+
bindingNames,
17+
// TODO(Babel 8): Remove this
18+
includeUpdateExpression: boolean = true,
19+
) {
1520
path.traverse(simpleAssignmentVisitor, {
1621
scope: path.scope,
1722
bindingNames,
1823
seen: new WeakSet(),
24+
includeUpdateExpression,
1925
});
2026
}
2127

2228
const simpleAssignmentVisitor = {
29+
// TODO(Babel 8): Remove UpdateExpression
2330
UpdateExpression: {
2431
exit(path) {
25-
const { scope, bindingNames } = this;
32+
const { scope, bindingNames, includeUpdateExpression } = this;
33+
if (!includeUpdateExpression) {
34+
return;
35+
}
2636

2737
const arg = path.get("argument");
2838
if (!arg.isIdentifier()) return;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import * as babel from "@babel/core";
2+
import simplifyAccess from "../lib/index.js";
3+
4+
const plugin = (_api, options) => {
5+
const { includeUpdateExpression, bindingNames } = options;
6+
7+
return {
8+
visitor: {
9+
Program(path) {
10+
simplifyAccess.default(
11+
path,
12+
new Set(bindingNames),
13+
includeUpdateExpression,
14+
);
15+
},
16+
},
17+
};
18+
};
19+
20+
it("simplifyAccess with default config", function () {
21+
const code = `
22+
let a = foo++;
23+
a = ++foo;
24+
foo++;
25+
++foo;
26+
++a;
27+
a++;
28+
foo = a++;
29+
foo = ++a;
30+
31+
let b = bar--;
32+
b = --bar;
33+
bar--;
34+
--bar;
35+
--b;
36+
b--;
37+
bar = b--;
38+
bar = --b;
39+
40+
let c = baz += 1;
41+
baz += 1;
42+
c += 1;
43+
44+
function f() {
45+
let foo = 1;
46+
let a = foo++;
47+
a = ++foo;
48+
foo++;
49+
++foo;
50+
++a;
51+
a++;
52+
foo = a++;
53+
foo = ++a;
54+
}
55+
`;
56+
57+
const output = babel.transformSync(code, {
58+
configFile: false,
59+
ast: false,
60+
plugins: [[plugin, { bindingNames: ["foo", "bar", "baz"] }]],
61+
}).code;
62+
63+
expect(output).toMatchInlineSnapshot(`
64+
"var _foo, _bar;
65+
66+
let a = (_foo = +foo, foo = _foo + 1, _foo);
67+
a = foo = +foo + 1;
68+
foo = foo + 1;
69+
foo = foo + 1;
70+
++a;
71+
a++;
72+
foo = a++;
73+
foo = ++a;
74+
let b = (_bar = +bar, bar = _bar - 1, _bar);
75+
b = bar = +bar - 1;
76+
bar = bar - 1;
77+
bar = bar - 1;
78+
--b;
79+
b--;
80+
bar = b--;
81+
bar = --b;
82+
let c = baz = baz + 1;
83+
baz = baz + 1;
84+
c += 1;
85+
86+
function f() {
87+
let foo = 1;
88+
let a = foo++;
89+
a = ++foo;
90+
foo++;
91+
++foo;
92+
++a;
93+
a++;
94+
foo = a++;
95+
foo = ++a;
96+
}"
97+
`);
98+
});
99+
100+
it("simplifyAccess with includeUpdateExpression=false", function () {
101+
const code = `
102+
let a = foo++;
103+
a = ++foo;
104+
foo++;
105+
++foo;
106+
++a;
107+
a++;
108+
foo = a++;
109+
foo = ++a;
110+
111+
let b = bar--;
112+
b = --bar;
113+
bar--;
114+
--bar;
115+
--b;
116+
b--;
117+
bar = b--;
118+
bar = --b;
119+
120+
let c = baz += 1;
121+
baz += 1;
122+
c += 1;
123+
124+
function f() {
125+
let foo = 1;
126+
let a = foo++;
127+
a = ++foo;
128+
foo++;
129+
++foo;
130+
++a;
131+
a++;
132+
foo = a++;
133+
foo = ++a;
134+
}
135+
`;
136+
137+
const output = babel.transformSync(code, {
138+
configFile: false,
139+
ast: false,
140+
plugins: [
141+
[
142+
plugin,
143+
{ includeUpdateExpression: false, bindingNames: ["foo", "bar", "baz"] },
144+
],
145+
],
146+
}).code;
147+
148+
expect(output).toMatchInlineSnapshot(`
149+
"let a = foo++;
150+
a = ++foo;
151+
foo++;
152+
++foo;
153+
++a;
154+
a++;
155+
foo = a++;
156+
foo = ++a;
157+
let b = bar--;
158+
b = --bar;
159+
bar--;
160+
--bar;
161+
--b;
162+
b--;
163+
bar = b--;
164+
bar = --b;
165+
let c = baz = baz + 1;
166+
baz = baz + 1;
167+
c += 1;
168+
169+
function f() {
170+
let foo = 1;
171+
let a = foo++;
172+
a = ++foo;
173+
foo++;
174+
++foo;
175+
++a;
176+
a++;
177+
foo = a++;
178+
foo = ++a;
179+
}"
180+
`);
181+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "type": "module" }

packages/babel-plugin-transform-modules-amd/test/fixtures/amd/remap/output.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ define(["exports"], function (_exports) {
55
value: true
66
});
77
_exports.test = _exports.f = _exports.e = _exports.c = _exports.a = void 0;
8+
9+
var _test;
10+
811
var test = 2;
912
_exports.test = test;
1013
_exports.test = test = 5;
11-
_exports.test = test = test + 1;
14+
_test = test++, _exports.test = test, _test;
1215

1316
(function () {
1417
var test = 2;

packages/babel-plugin-transform-modules-amd/test/fixtures/loose/remap/output.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ define(["exports"], function (_exports) {
33

44
_exports.__esModule = true;
55
_exports.test = _exports.f = _exports.e = _exports.c = _exports.a = void 0;
6+
7+
var _test;
8+
69
var test = 2;
710
_exports.test = test;
811
_exports.test = test = 5;
9-
_exports.test = test = test + 1;
12+
_test = test++, _exports.test = test, _test;
1013

1114
(function () {
1215
var test = 2;

packages/babel-plugin-transform-modules-commonjs/src/index.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,26 @@ export default declare((api, options) => {
8989
path.replaceWith(getAssertion(localName));
9090
},
9191

92+
UpdateExpression(path) {
93+
const arg = path.get("argument");
94+
const localName = arg.node.name;
95+
if (localName !== "module" && localName !== "exports") return;
96+
97+
const localBinding = path.scope.getBinding(localName);
98+
const rootBinding = this.scope.getBinding(localName);
99+
100+
// redeclared in this scope
101+
if (rootBinding !== localBinding) return;
102+
103+
path.replaceWith(
104+
t.assignmentExpression(
105+
path.node.operator[0] + "=",
106+
arg.node,
107+
getAssertion(localName),
108+
),
109+
);
110+
},
111+
92112
AssignmentExpression(path) {
93113
const left = path.get("left");
94114
if (left.isIdentifier()) {
@@ -162,7 +182,7 @@ export default declare((api, options) => {
162182
// These objects are specific to CommonJS and are not available in
163183
// real ES6 implementations.
164184
if (!allowCommonJSExports) {
165-
simplifyAccess(path, new Set(["module", "exports"]));
185+
simplifyAccess(path, new Set(["module", "exports"]), false);
166186
path.traverse(moduleExportsVisitor, {
167187
scope: path.scope,
168188
});

packages/babel-plugin-transform-modules-commonjs/test/fixtures/interop-loose/remap/output.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
exports.__esModule = true;
44
exports.test = exports.f = exports.e = exports.c = exports.a = void 0;
5+
6+
var _test;
7+
58
var test = 2;
69
exports.test = test;
710
exports.test = test = 5;
8-
exports.test = test = test + 1;
11+
_test = test++, exports.test = test, _test;
912

1013
(function () {
1114
var test = 2;

0 commit comments

Comments
 (0)