Fork of the Fernflower decompiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fernflower/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java

185 lines
5.0 KiB

// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
public final class LoopExtractHelper {
public static boolean extractLoops(Statement root) {
boolean res = (extractLoopsRec(root) != 0);
if (res) {
SequenceHelper.condenseSequences(root);
}
return res;
}
private static int extractLoopsRec(Statement stat) {
boolean res = false;
while (true) {
boolean updated = false;
for (Statement st : stat.getStats()) {
int extr = extractLoopsRec(st);
res |= (extr != 0);
if (extr == 2) {
updated = true;
break;
}
}
if (!updated) {
break;
}
}
if (stat.type == Statement.TYPE_DO) {
if (extractLoop((DoStatement)stat)) {
return 2;
}
}
return res ? 1 : 0;
}
private static boolean extractLoop(DoStatement stat) {
if (stat.getLooptype() != DoStatement.LOOP_DO) {
return false;
}
for (StatEdge edge : stat.getLabelEdges()) {
if (edge.getType() != StatEdge.TYPE_CONTINUE && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) {
return false;
}
}
return extractLastIf(stat) || extractFirstIf(stat);
}
private static boolean extractLastIf(DoStatement stat) {
// search for an if condition at the end of the loop
Statement last = stat.getFirst();
while (last.type == Statement.TYPE_SEQUENCE) {
last = last.getStats().getLast();
}
if (last.type == Statement.TYPE_IF) {
IfStatement lastif = (IfStatement)last;
if (lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() != null) {
Statement ifstat = lastif.getIfstat();
StatEdge elseedge = lastif.getAllSuccessorEdges().get(0);
if (elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat) {
Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD);
set.remove(last);
if (set.isEmpty()) { // no direct continues in a do{}while loop
if (isExternStatement(stat, ifstat, ifstat)) {
extractIfBlock(stat, lastif);
return true;
}
}
}
}
}
return false;
}
private static boolean extractFirstIf(DoStatement stat) {
// search for an if condition at the entrance of the loop
Statement first = stat.getFirst();
while (first.type == Statement.TYPE_SEQUENCE) {
first = first.getFirst();
}
// found an if statement
if (first.type == Statement.TYPE_IF) {
IfStatement firstif = (IfStatement)first;
if (firstif.getFirst().getExprents().isEmpty()) {
if (firstif.iftype == IfStatement.IFTYPE_IF && firstif.getIfstat() != null) {
Statement ifstat = firstif.getIfstat();
if (isExternStatement(stat, ifstat, ifstat)) {
extractIfBlock(stat, firstif);
return true;
}
}
}
}
return false;
}
private static boolean isExternStatement(DoStatement loop, Statement block, Statement stat) {
for (StatEdge edge : stat.getAllSuccessorEdges()) {
if (loop.containsStatement(edge.getDestination()) &&
!block.containsStatement(edge.getDestination())) {
return false;
}
}
for (Statement st : stat.getStats()) {
if (!isExternStatement(loop, block, st)) {
return false;
}
}
return true;
}
private static void extractIfBlock(DoStatement loop, IfStatement ifstat) {
Statement target = ifstat.getIfstat();
StatEdge ifedge = ifstat.getIfEdge();
ifstat.setIfstat(null);
ifedge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, ifedge, StatEdge.TYPE_BREAK);
ifedge.closure = loop;
ifstat.getStats().removeWithKey(target.id);
loop.addLabeledEdge(ifedge);
SequenceStatement block = new SequenceStatement(Arrays.asList(loop, target));
loop.getParent().replaceStatement(loop, block);
block.setAllParent();
loop.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, loop, target));
for (StatEdge edge : new ArrayList<>(block.getLabelEdges())) {
if (edge.getType() == StatEdge.TYPE_CONTINUE || edge == ifedge) {
loop.addLabeledEdge(edge);
}
}
for (StatEdge edge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) {
if (loop.containsStatementStrict(edge.getSource())) {
block.removePredecessor(edge);
edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, loop);
loop.addPredecessor(edge);
}
}
}
}