Fixed: #2651 Loop breaks detection

This commit is contained in:
Jindra Petřík
2026-03-04 08:29:46 +01:00
parent 29ead369bf
commit d487ef7064
12 changed files with 108 additions and 56 deletions

View File

@@ -1878,14 +1878,14 @@ public class AVM2Graph extends Graph {
if (caseBodyParts.size() == 2) {
boolean isIf = false;
for (GraphPart r : part.refs) {
if (r != origPart && !origPart.leadsTo(localData, this, code, r, loops, throwStates)) {
if (r != origPart && !origPart.leadsTo(localData, this, code, r, loops, throwStates, false)) {
isIf = true;
break;
}
}
if (!isIf) {
for (GraphPart r : caseBodyParts.get(1).refs) {
if (r != origPart && !origPart.leadsTo(localData, this, code, r, loops, throwStates)) {
if (r != origPart && !origPart.leadsTo(localData, this, code, r, loops, throwStates, false)) {
isIf = true;
break;
}

View File

@@ -581,7 +581,7 @@ public class Graph {
if (q == p) {
continue;
}
if (!q.leadsTo(localData, this, code, p, loops, throwStates)) {
if (!q.leadsTo(localData, this, code, p, loops, throwStates, false)) {
common = false;
break;
}
@@ -666,10 +666,10 @@ public class Graph {
int levelCompare = o2.level - o1.level;
if (levelCompare == 0) {
try {
if (o1.part.leadsTo(localData, Graph.this, code, o2.part, loops, throwStates)) {
if (o1.part.leadsTo(localData, Graph.this, code, o2.part, loops, throwStates, false)) {
return -1;
}
if (o2.part.leadsTo(localData, Graph.this, code, o1.part, loops, throwStates)) {
if (o2.part.leadsTo(localData, Graph.this, code, o1.part, loops, throwStates, false)) {
return 1;
}
return 0;
@@ -732,7 +732,7 @@ public class Graph {
if (visited.contains(n)) {
continue;
}
if (!n.leadsTo(localData, this, code, r, loops, throwStates)) {
if (!n.leadsTo(localData, this, code, r, loops, throwStates, false)) {
common = false;
break loopprocess;
}
@@ -1738,7 +1738,7 @@ public class Graph {
}
}
}
if (el.loopContinue.leadsTo(localData, this, code, r, loops, throwStates)) {
if (el.loopContinue.leadsTo(localData, this, code, r, loops, throwStates, false)) {
el.backEdges.add(r);
}
}
@@ -2713,7 +2713,7 @@ public class Graph {
}
try {
if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2, throwStates)) {
if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2, throwStates, false)) {
if (lastP1.breakCandidatesLocked == 0) {
if (debugGetLoops) {
System.err.println("added breakCandidate " + part + " to " + lastP1);
@@ -2746,7 +2746,7 @@ public class Graph {
}
part.level = level;
isLoop = part.leadsTo(localData, this, code, part, loops, throwStates);
isLoop = part.leadsTo(localData, this, code, part, loops, throwStates, false);
currentLoop = null;
if (isLoop) {
currentLoop = new Loop(loops.size(), part, null);
@@ -2906,7 +2906,7 @@ public class Graph {
for (int c = 0; c < currentLoop.breakCandidates.size(); c++) {
GraphPart cand = currentLoop.breakCandidates.get(c);
GraphPart sp = currentLoop.stopParts.get(currentLoop.stopParts.size() - 1);
if (cand == sp || cand.leadsTo(localData, this, code, sp, new ArrayList<>() /*ignore existing loop states*/, throwStates)) {
if (cand == sp || cand.leadsTo(localData, this, code, sp, new ArrayList<>() /*ignore existing loop states*/, throwStates, false)) {
breakCandidatesLeft.add(c);
}
}
@@ -2932,7 +2932,7 @@ public class Graph {
if (cand == cand2) {
continue;
}
if (cand.leadsTo(localData, this, code, cand2, loops, throwStates)) {
if (cand.leadsTo(localData, this, code, cand2, loops, throwStates, false)) {
int curLevl = currentLoop.breakCandidatesLevels.get(c1);
int curLev2 = currentLoop.breakCandidatesLevels.get(c2);
@@ -3882,7 +3882,7 @@ public class Graph {
continue;
}
// also #2636
if (!part.leadsTo(localData, this, code, r, loops, throwStates)) {
if (!part.leadsTo(localData, this, code, r, loops, throwStates, true /*IMPORTANT*/)) {
continue;
}
@@ -3901,7 +3901,7 @@ public class Graph {
if (v.contains(n)) {
continue;
}
if (!n.leadsTo(localData, this, code, next, loops, throwStates)) {
if (!n.leadsTo(localData, this, code, next, loops, throwStates, true /*IMPORTANT*/)) {
GraphPart n2 = getCommonPart(localData, r, Arrays.asList(next, n), loops, throwStates);
if (n2 != null) {
//System.err.println("Found block: start = " + part + ", break = " + n2 + ", exit = " + r);
@@ -4834,7 +4834,7 @@ public class Graph {
*/
//must go backwards to hit case 2, not case 1
for (int i = caseBodyParts.size() - 1; i >= 0; i--) {
if (caseBodyParts.get(i).leadsTo(localData, this, code, defaultPart, loops, throwStates)) {
if (caseBodyParts.get(i).leadsTo(localData, this, code, defaultPart, loops, throwStates, false)) {
DefaultItem di = new DefaultItem(dialect);
caseValuesMap.add(i + 1, di);
caseBodyParts.add(i + 1, defaultPart);
@@ -4855,7 +4855,7 @@ public class Graph {
trace("2");
*/
for (int i = 0; i < caseBodyParts.size(); i++) {
if (defaultPart.leadsTo(localData, this, code, caseBodyParts.get(i), loops, throwStates)) {
if (defaultPart.leadsTo(localData, this, code, caseBodyParts.get(i), loops, throwStates, false)) {
DefaultItem di = new DefaultItem(dialect);
caseValuesMap.add(i, di);
caseBodyParts.add(i, defaultPart);
@@ -4905,8 +4905,8 @@ public class Graph {
GraphPart b = caseBodies.get(i);
for (int j = i + 1; j < caseBodies.size(); j++) {
GraphPart b2 = caseBodies.get(j);
if (b2.leadsTo(localData, this, code, b, loops, throwStates)) {
if (b.leadsTo(localData, this, code, b2, loops, throwStates)) { //unstructured code
if (b2.leadsTo(localData, this, code, b, loops, throwStates, false)) {
if (b.leadsTo(localData, this, code, b2, loops, throwStates, false)) { //unstructured code
continue;
}
caseBodies.remove(j);
@@ -4914,7 +4914,7 @@ public class Graph {
i--;
continue loopi;
} else if (j > i + 1) {
if (b.leadsTo(localData, this, code, b2, loops, throwStates)) {
if (b.leadsTo(localData, this, code, b2, loops, throwStates, false)) {
caseBodies.remove(j);
caseBodies.add(i + 1, b2);
continue loopi;
@@ -4932,7 +4932,7 @@ public class Graph {
List<GraphTargetItem> currentCaseCommands = new ArrayList<>();
boolean willHaveBreak = false;
if (i < caseBodies.size() - 1) {
if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops, throwStates)) {
if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops, throwStates, false)) {
willHaveBreak = true;
}
}

View File

@@ -140,17 +140,18 @@ public class GraphPart implements Serializable {
* @param visited Visited parts
* @param loops Loops
* @param throwStates Throw states
* @param firstCanBeLoopContinue Can entry point be loop continue?
* @return True if this part leads to the other part
* @throws InterruptedException On interrupt
*/
private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet<GraphPart> visited, List<Loop> loops, List<ThrowState> throwStates) throws InterruptedException {
private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet<GraphPart> visited, List<Loop> loops, List<ThrowState> throwStates, boolean firstCanBeLoopContinue) throws InterruptedException {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
Stack<GraphPart> todo = new Stack<>();
todo.push(this);
boolean first = true;
boolean first = firstCanBeLoopContinue; //This is big HACK of how to get always-break working
looptodo:
while (!todo.isEmpty()) {
@@ -224,14 +225,15 @@ public class GraphPart implements Serializable {
* @param part Part to check
* @param loops Loops
* @param throwStates Throw states
* @param firstCanBeLoopContinue Can entry point be loop continue?
* @return True if this part leads to the other part
* @throws InterruptedException On interrupt
*/
public boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List<Loop> loops, List<ThrowState> throwStates) throws InterruptedException {
public boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List<Loop> loops, List<ThrowState> throwStates, boolean firstCanBeLoopContinue) throws InterruptedException {
for (Loop l : loops) {
l.leadsToMark = 0;
}
return leadsTo(localData, gr, code, null /*???*/, part, new HashSet<>(), loops, throwStates);
return leadsTo(localData, gr, code, null /*???*/, part, new HashSet<>(), loops, throwStates, firstCanBeLoopContinue);
}
/**