Skip to content

Commit fc66e99

Browse files
Fix for binding shadowing outer var with loop closure (#15309)
* Fix for binding shadowing outer var with loop closure Fix updating of non-captured variable with loop closure * Rename
1 parent 64d5a92 commit fc66e99

File tree

17 files changed

+158
-12
lines changed

17 files changed

+158
-12
lines changed

packages/babel-plugin-transform-block-scoping/src/index.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,32 @@ export default declare((api, opts: Options) => {
7171

7272
if (headPath && isBlockScoped(headPath.node)) {
7373
const names = Object.keys(headPath.getBindingIdentifiers());
74+
const headScope = headPath.scope;
7475

75-
for (const name of names) {
76+
for (let name of names) {
7677
if (bodyScope?.hasOwnBinding(name)) continue; // shadowed
7778

78-
let binding = headPath.scope.getOwnBinding(name);
79+
let binding = headScope.getOwnBinding(name);
7980
if (!binding) {
80-
headPath.scope.crawl();
81-
binding = headPath.scope.getOwnBinding(name);
81+
headScope.crawl();
82+
binding = headScope.getOwnBinding(name);
8283
}
8384
const { usages, capturedInClosure, hasConstantViolations } =
8485
getUsageInBody(binding, path);
8586

8687
if (capturedInClosure) {
8788
markNeedsBodyWrap();
8889
captured.push(name);
90+
} else if (headScope.parent.hasBinding(name)) {
91+
// If the binding is not captured, there is no need
92+
// of adding it to the closure param. However, rename
93+
// it if it shadows an outer binding, because the
94+
// closure will be moved to an outer level.
95+
const newName = headScope.generateUid(name);
96+
headPath.scope.rename(name, newName);
97+
name = newName;
8998
}
99+
90100
if (isForStatement && hasConstantViolations) {
91101
updatedBindingsUsages.set(name, usages);
92102
}

packages/babel-plugin-transform-block-scoping/src/loop.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,23 +177,21 @@ export function wrapLoopBody(
177177
const callArgs = [];
178178
const closureParams = [];
179179
const updater = [];
180-
for (const name of captured) {
180+
for (const [name, updatedUsage] of updatedBindingsUsages) {
181181
callArgs.push(t.identifier(name));
182182

183-
const updatedUsage = updatedBindingsUsages.get(name);
184-
if (!updatedUsage) {
185-
// Not updated, re-use the same name
186-
closureParams.push(t.identifier(name));
187-
continue;
188-
}
189-
190183
const innerName = loopPath.scope.generateUid(name);
191184
closureParams.push(t.identifier(innerName));
192185
updater.push(
193186
t.assignmentExpression("=", t.identifier(name), t.identifier(innerName)),
194187
);
195188
for (const path of updatedUsage) path.replaceWith(t.identifier(innerName));
196189
}
190+
for (const name of captured) {
191+
if (updatedBindingsUsages.has(name)) continue; // already injected
192+
callArgs.push(t.identifier(name));
193+
closureParams.push(t.identifier(name));
194+
}
197195

198196
const id = loopPath.scope.generateUid("loop");
199197
const fn = t.functionExpression(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
let res = [];
2+
3+
for (let i = 1; i < 6; i++) {
4+
let y = i;
5+
res.push((() => y)());
6+
i++;
7+
}
8+
9+
expect(res).toEqual([1, 3, 5]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
for (let i = 4; i < 6; i++) {
2+
let y = i;
3+
() => y;
4+
i++;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
var _loop = function (_i) {
2+
var y = _i;
3+
(function () {
4+
return y;
5+
});
6+
_i++;
7+
i = _i;
8+
};
9+
for (var i = 4; i < 6; i++) {
10+
_loop(i);
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
let res = [];
2+
3+
let i = 0;
4+
for (let i = 4; i < 6; i++) {
5+
res.push((() => i)());
6+
}
7+
8+
expect(res).toEqual([4, 5]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
let i;
2+
for (let i = 4; i < 6; i++) {
3+
() => i;
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
var i;
2+
var _loop = function (i) {
3+
(function () {
4+
return i;
5+
});
6+
};
7+
for (var _i = 4; _i < 6; _i++) {
8+
_loop(_i);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
let res = [];
2+
3+
let i = 0;
4+
for (let i = 1; i < 6; i++) {
5+
let y = i;
6+
res.push((() => y)());
7+
i++;
8+
}
9+
10+
expect(res).toEqual([1, 3, 5]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
let i;
2+
for (let i = 4; i < 6; i++) {
3+
let y = i;
4+
() => y;
5+
i++;
6+
}

0 commit comments

Comments
 (0)