/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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 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; } } if (!extractLastIf(stat)) { return extractFirstIf(stat); } else { return true; } } 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); } } } }