This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new e73492692d GROOVY-11739: `continue` jumps to condition of `do`/`while` 
loop
e73492692d is described below

commit e73492692deff0dddf27f96e7d0eea0edbc3a4e8
Author: Eric Milles <eric.mil...@thomsonreuters.com>
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 d6f0bbde9e..9c53893348 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/StatementWriter.java
@@ -262,12 +262,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);
@@ -276,26 +276,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
+}

Reply via email to