Skip to content

Commit

Permalink
Add a scope in while loops
Browse files Browse the repository at this point in the history
Summary:
Emit a scope in while loops, so that lexically scoped declarations in
the loop will have a different instance on each iteration.

Reviewed By: avp

Differential Revision: D56026167

fbshipit-source-id: 77cadc4e4d72b86f820bb183a5bd4ccbde498029
  • Loading branch information
neildhar authored and facebook-github-bot committed Nov 13, 2024
1 parent c477182 commit 1bc4a01
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
18 changes: 18 additions & 0 deletions lib/IRGen/ESTreeIRGen-stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,25 @@ void ESTreeIRGen::genWhileLoop(ESTree::WhileStatementNode *loop) {
genExpressionBranch(loop->_test, bodyBlock, exitBlock, nullptr);
// Generate the body.
Builder.setInsertionBlock(bodyBlock);

// Save the outer scope in case we create an inner scope.
auto *outerScope = curFunction()->curScope;

// If the body captures, create an inner scope for it. Note that unlike for
// loops, we don't care whether the test expression captures, since the loop
// does not create variables visible in the test expression.
if (Mod->getContext().getEnableES6BlockScoping() &&
!treeDoesNotCapture(loop->_body)) {
auto *bodyVarScope = curFunction()->getOrCreateInnerVariableScope(loop);
auto *bodyScope = Builder.createCreateScopeInst(bodyVarScope, outerScope);
curFunction()->curScope = bodyScope;
}

genStatement(loop->_body);

// Restore the outer scope.
curFunction()->curScope = outerScope;

// After executing the content of the body, jump to the post test block.
Builder.createBranchInst(postTestBlock);
Builder.setInsertionBlock(postTestBlock);
Expand Down
44 changes: 44 additions & 0 deletions test/hermes/while-finally-nested.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: %hermes %s -Xes6-block-scoping | %FileCheck --match-full-lines %s
// RUN: %hermes -Xes6-block-scoping -lazy %s | %FileCheck --match-full-lines %s
// RUN: %shermes -Xes6-block-scoping -exec %s | %FileCheck --match-full-lines %s

let arr = (function (){
let arr = [];
try {
while (true) {
let x = 42;
// Make sure there is a capture to force a scope.
arr.push(()=>x);
return arr;
}
} finally {
// Test that emitting this finally block at the location of the return above
// (which is a different scope in the IR) works correctly.
while (arr.length < 10) {
let x = arr.length;
arr.push(()=>x);
}
}
})();

for (v of arr){
print(v());
}

// CHECK: 42
// CHECK-NEXT: 1
// CHECK-NEXT: 2
// CHECK-NEXT: 3
// CHECK-NEXT: 4
// CHECK-NEXT: 5
// CHECK-NEXT: 6
// CHECK-NEXT: 7
// CHECK-NEXT: 8
// CHECK-NEXT: 9
34 changes: 34 additions & 0 deletions test/hermes/while-let-capture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: %hermes -Xes6-block-scoping %s | %FileCheck --match-full-lines %s
// RUN: %shermes -Xes6-block-scoping -exec %s | %FileCheck --match-full-lines %s

(function (){
let arr = [];
let i = 0;
while (i < 10) {
let j = i;
arr.push(()=>j);
i++;
}

for(f of arr) {
print(f());
}
})();

// CHECK: 0
// CHECK-NEXT: 1
// CHECK-NEXT: 2
// CHECK-NEXT: 3
// CHECK-NEXT: 4
// CHECK-NEXT: 5
// CHECK-NEXT: 6
// CHECK-NEXT: 7
// CHECK-NEXT: 8
// CHECK-NEXT: 9

0 comments on commit 1bc4a01

Please sign in to comment.