This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch GROOVY_5_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_5_0_X by this push:
new 4e27160adf GROOVY-11739: `continue` jumps to condition of `do`/`while`
loop
4e27160adf is described below
commit 4e27160adfc3f9d76f67f2d6e77e7dec33942a6d
Author: Eric Milles <[email protected]>
AuthorDate: Sun Aug 24 08:05:52 2025 -0500
GROOVY-11739: `continue` jumps to condition of `do`/`while` loop
(cherry picked from commit 8328a76c5a42ea16f8b40f449e35ff748f0f161d)
---
.../groovy/classgen/asm/StatementWriter.java | 27 +++---
.../groovy/groovy/BreakContinueLabelTest.groovy | 101 +++++++++++++--------
2 files changed, 77 insertions(+), 51 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
b/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
index 0749aad049..2c2d8b0100 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
@@ -269,12 +269,12 @@ public class StatementWriter {
controller.getAcg().onLineNumber(statement, "visitWhileLoop");
writeStatementLabel(statement);
- MethodVisitor mv = controller.getMethodVisitor();
-
- controller.getCompileStack().pushLoop(statement.getStatementLabels());
- Label continueLabel = controller.getCompileStack().getContinueLabel();
- Label breakLabel = controller.getCompileStack().getBreakLabel();
+ CompileStack compileStack = controller.getCompileStack();
+ compileStack.pushLoop(statement.getStatementLabels());
+ Label continueLabel = compileStack.getContinueLabel();
+ Label breakLabel = compileStack.getBreakLabel();
+ MethodVisitor mv = controller.getMethodVisitor();
mv.visitLabel(continueLabel);
visitConditionOfLoopingStatement(statement.getBooleanExpression(),
breakLabel, mv);
@@ -283,26 +283,29 @@ public class StatementWriter {
mv.visitJumpInsn(GOTO, continueLabel);
mv.visitLabel(breakLabel);
- controller.getCompileStack().pop();
+ compileStack.pop();
}
public void writeDoWhileLoop(final DoWhileStatement statement) {
writeStatementLabel(statement);
- controller.getCompileStack().pushLoop(statement.getStatementLabels());
- Label continueLabel = controller.getCompileStack().getContinueLabel();
- Label breakLabel = controller.getCompileStack().getBreakLabel();
+ CompileStack compileStack = controller.getCompileStack();
+ compileStack.pushLoop(statement.getStatementLabels());
+ Label continueLabel = compileStack.getContinueLabel();
+ Label breakLabel = compileStack.getBreakLabel();
+ Label blockLabel = new Label();
MethodVisitor mv = controller.getMethodVisitor();
- mv.visitLabel(continueLabel);
+ mv.visitLabel(blockLabel);
statement.getLoopBlock().visit(controller.getAcg());
+ mv.visitLabel(continueLabel); // GROOVY-11739: continue jumps to
condition
visitConditionOfLoopingStatement(statement.getBooleanExpression(),
breakLabel, mv);
- mv.visitJumpInsn(GOTO, continueLabel);
+ mv.visitJumpInsn(GOTO, blockLabel);
mv.visitLabel(breakLabel);
- controller.getCompileStack().pop();
+ compileStack.pop();
}
public void writeIfElse(final IfStatement statement) {
diff --git a/src/test/groovy/groovy/BreakContinueLabelTest.groovy
b/src/test/groovy/groovy/BreakContinueLabelTest.groovy
index a993f20382..703fb7ffdb 100644
--- a/src/test/groovy/groovy/BreakContinueLabelTest.groovy
+++ b/src/test/groovy/groovy/BreakContinueLabelTest.groovy
@@ -18,18 +18,20 @@
*/
package groovy
-import gls.CompilableTestSupport
+import org.junit.jupiter.api.Test
-/**
- * todo: add BreakContinueLabelWithClosureTest (when break is used to return
from a Closure)
- */
-class BreakContinueLabelTest extends CompilableTestSupport {
+import static org.junit.jupiter.api.Assertions.fail
+
+final class BreakContinueLabelTest {
+ @Test
void testDeclareSimpleLabel() {
label_1: assert true
label_2:
assert true
}
+
+ @Test
void testBreakLabelInSimpleForLoop() {
label_1: for (i in [1]) {
break label_1
@@ -37,28 +39,31 @@ class BreakContinueLabelTest extends CompilableTestSupport {
}
}
+ @Test
void testBreakLabelInNestedForLoop() {
label: for (i in [1]) {
for (j in [1]){
break label
- assert false, 'did not break inner loop'
+ assert false : 'did not break inner loop'
}
- assert false, 'did not break outer loop'
+ assert false : 'did not break outer loop'
}
}
+ @Test
void testUnlabelledBreakInNestedForLoop() {
def reached = false
for (i in [1]) {
- for (j in [1]){
+ for (j in [1]) {
break
- assert false, 'did not break inner loop'
+ assert false : 'did not break inner loop'
}
reached = true
}
- assert reached, 'must not break outer loop'
+ assert reached : 'must not break outer loop'
}
+ @Test
void testBreakLabelInSimpleWhileLoop() {
label_1: while (true) {
break label_1
@@ -66,62 +71,80 @@ class BreakContinueLabelTest extends CompilableTestSupport {
}
}
+ @Test
void testBreakLabelInNestedWhileLoop() {
def count = 0
label: while (count < 1) {
count++
- while (true){
+ while (true) {
break label
- assert false, 'did not break inner loop'
+ assert false : 'did not break inner loop'
}
- assert false, 'did not break outer loop'
+ assert false : 'did not break outer loop'
}
}
+ @Test
void testBreakLabelInNestedMixedForAndWhileLoop() {
def count = 0
label_1: while (count < 1) {
count++
- for (i in [1]){
+ for (i in [1]) {
break label_1
- assert false, 'did not break inner loop'
+ assert false : 'did not break inner loop'
}
- assert false, 'did not break outer loop'
+ assert false : 'did not break outer loop'
}
label_2: for (i in [1]) {
- while (true){
+ while (true) {
break label_2
- assert false, 'did not break inner loop'
+ assert false : 'did not break inner loop'
}
- assert false, 'did not break outer loop'
+ assert false : 'did not break outer loop'
}
}
+ // GROOVY-11739
+ @Test
+ void testUnlabelledContinueWithinDoWhileLoop() {
+ int i = 0;
+ do {
+ i += 1
+ if (i > 1000) break // prevent infinite loop
+ continue // control should pass to condition
+ } while (i < 100)
+
+ assert i == 100
+ }
+
+ @Test
void testUnlabelledContinueInNestedForLoop() {
def log = ''
for (i in [1,2]) {
log += i
- for (j in [3,4]){
+ for (j in [3,4]) {
if (j==3) continue
log += j
}
}
- assertEquals '1424',log
+ assert log == '1424'
}
+ @Test
void testContinueLabelInNestedForLoop() {
def log = ''
label: for (i in [1,2]) {
log += i
- for (j in [3,4]){
+ for (j in [3,4]) {
if (j==4) continue label
log += j
}
log += 'never reached'
}
- assertEquals '1323',log
+ assert log == '1323'
}
+ @Test
void testBreakToLastLabelSucceeds() {
one:
two:
@@ -132,25 +155,25 @@ class BreakContinueLabelTest extends
CompilableTestSupport {
}
}
+ @Test
void testMultipleLabelSupport() {
- assertScript """
- def visited = []
- label1:
- label2:
- label3:
- for (int i = 0; i < 9; i++) {
- visited << i
- if (i == 1) continue label1
- visited << 10 + i
- if (i == 3) continue label2
- visited << 100 + i
- if (i == 5) break label3
- }
- assert visited == [0, 10, 100, 1, 2, 12, 102, 3, 13, 4, 14, 104,
5, 15, 105]
- """
+ def visited = []
+ label1:
+ label2:
+ label3:
+ for (int i = 0; i < 9; i++) {
+ visited << i
+ if (i == 1) continue label1
+ visited << 10 + i
+ if (i == 3) continue label2
+ visited << 100 + i
+ if (i == 5) break label3
+ }
+ assert visited == [0, 10, 100, 1, 2, 12, 102, 3, 13, 4, 14, 104, 5,
15, 105]
}
// this is in accordance with Java; Spock Framework relies on this
+ @Test
void testLabelCanOccurMultipleTimesInSameScope() {
one:
for (i in 1..2) {
@@ -163,4 +186,4 @@ class BreakContinueLabelTest extends CompilableTestSupport {
fail()
}
}
-}
\ No newline at end of file
+}