/* * 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.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; import org.jetbrains.java.decompiler.modules.decompiler.stats.*; import java.util.ArrayList; import java.util.List; import java.util.Set; public class MergeHelper { public static void enhanceLoops(Statement root) { while (enhanceLoopsRec(root)) ; SequenceHelper.condenseSequences(root); } private static boolean enhanceLoopsRec(Statement stat) { boolean res = false; for (Statement st : stat.getStats()) { if (st.getExprents() == null) { res |= enhanceLoopsRec(st); } } if (stat.type == Statement.TYPE_DO) { res |= enhanceLoop((DoStatement)stat); } return res; } private static boolean enhanceLoop(DoStatement stat) { int oldloop = stat.getLooptype(); switch (oldloop) { case DoStatement.LOOP_DO: // identify a while loop if (matchWhile(stat)) { // identify a for loop - subtype of while matchFor(stat); } else { // identify a do{}while loop matchDoWhile(stat); } break; case DoStatement.LOOP_WHILE: matchFor(stat); } return (stat.getLooptype() != oldloop); } private static boolean matchDoWhile(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) { StatEdge ifedge = lastif.getIfEdge(); StatEdge elseedge = lastif.getAllSuccessorEdges().get(0); if ((ifedge.getType() == StatEdge.TYPE_BREAK && elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat && isDirectPath(stat, ifedge.getDestination())) || (ifedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.getType() == StatEdge.TYPE_BREAK && ifedge.closure == stat && isDirectPath(stat, elseedge.getDestination()))) { Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); set.remove(last); if (!set.isEmpty()) { return false; } stat.setLooptype(DoStatement.LOOP_DOWHILE); IfExprent ifexpr = (IfExprent)lastif.getHeadexprent().copy(); if (ifedge.getType() == StatEdge.TYPE_BREAK) { ifexpr.negateIf(); } stat.setConditionExprent(ifexpr.getCondition()); lastif.getFirst().removeSuccessor(ifedge); lastif.removeSuccessor(elseedge); // remove empty if if (lastif.getFirst().getExprents().isEmpty()) { removeLastEmptyStatement(stat, lastif); } else { lastif.setExprents(lastif.getFirst().getExprents()); StatEdge newedge = new StatEdge(StatEdge.TYPE_CONTINUE, lastif, stat); lastif.addSuccessor(newedge); stat.addLabeledEdge(newedge); } if (stat.getAllSuccessorEdges().isEmpty()) { StatEdge edge = elseedge.getType() == StatEdge.TYPE_CONTINUE ? ifedge : elseedge; edge.setSource(stat); if (edge.closure == stat) { edge.closure = stat.getParent(); } stat.addSuccessor(edge); } return true; } } } return false; } private static boolean matchWhile(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) { if (firstif.getIfstat() == null) { StatEdge ifedge = firstif.getIfEdge(); if (isDirectPath(stat, ifedge.getDestination())) { // exit condition identified stat.setLooptype(DoStatement.LOOP_WHILE); // negate condition (while header) IfExprent ifexpr = (IfExprent)firstif.getHeadexprent().copy(); ifexpr.negateIf(); stat.setConditionExprent(ifexpr.getCondition()); // remove edges firstif.getFirst().removeSuccessor(ifedge); firstif.removeSuccessor(firstif.getAllSuccessorEdges().get(0)); if (stat.getAllSuccessorEdges().isEmpty()) { ifedge.setSource(stat); if (ifedge.closure == stat) { ifedge.closure = stat.getParent(); } stat.addSuccessor(ifedge); } // remove empty if statement as it is now part of the loop if (firstif == stat.getFirst()) { BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); bstat.setExprents(new ArrayList<>()); stat.replaceStatement(firstif, bstat); } else { // precondition: sequence must contain more than one statement! Statement sequence = firstif.getParent(); sequence.getStats().removeWithKey(firstif.id); sequence.setFirst(sequence.getStats().get(0)); } return true; } } else { StatEdge elseedge = firstif.getAllSuccessorEdges().get(0); if (isDirectPath(stat, elseedge.getDestination())) { // exit condition identified stat.setLooptype(DoStatement.LOOP_WHILE); // no need to negate the while condition stat.setConditionExprent(((IfExprent)firstif.getHeadexprent().copy()).getCondition()); // remove edges StatEdge ifedge = firstif.getIfEdge(); firstif.getFirst().removeSuccessor(ifedge); firstif.removeSuccessor(elseedge); if (stat.getAllSuccessorEdges().isEmpty()) { elseedge.setSource(stat); if (elseedge.closure == stat) { elseedge.closure = stat.getParent(); } stat.addSuccessor(elseedge); } if (firstif.getIfstat() == null) { BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); bstat.setExprents(new ArrayList<>()); ifedge.setSource(bstat); bstat.addSuccessor(ifedge); stat.replaceStatement(firstif, bstat); } else { // replace the if statement with its content first.getParent().replaceStatement(first, firstif.getIfstat()); // lift closures for (StatEdge prededge : elseedge.getDestination().getPredecessorEdges(StatEdge.TYPE_BREAK)) { if (stat.containsStatementStrict(prededge.closure)) { stat.addLabeledEdge(prededge); } } LabelHelper.lowClosures(stat); } return true; } } } } } return false; } public static boolean isDirectPath(Statement stat, Statement endstat) { Set<Statement> setStat = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD); if (setStat.isEmpty()) { Statement parent = stat.getParent(); if (parent == null) { return false; } else { switch (parent.type) { case Statement.TYPE_ROOT: return endstat.type == Statement.TYPE_DUMMYEXIT; case Statement.TYPE_DO: return (endstat == parent); case Statement.TYPE_SWITCH: SwitchStatement swst = (SwitchStatement)parent; for (int i = 0; i < swst.getCaseStatements().size() - 1; i++) { Statement stt = swst.getCaseStatements().get(i); if (stt == stat) { Statement stnext = swst.getCaseStatements().get(i + 1); if (stnext.getExprents() != null && stnext.getExprents().isEmpty()) { stnext = stnext.getAllSuccessorEdges().get(0).getDestination(); } return (endstat == stnext); } } default: return isDirectPath(parent, endstat); } } } else { return setStat.contains(endstat); } } private static boolean matchFor(DoStatement stat) { Exprent lastDoExprent = null, initDoExprent = null; Statement lastData = null, preData = null; // get last exprent lastData = getLastDirectData(stat.getFirst()); if (lastData == null || lastData.getExprents().isEmpty()) { return false; } List<Exprent> lstExpr = lastData.getExprents(); lastDoExprent = lstExpr.get(lstExpr.size() - 1); boolean issingle = false; if (lstExpr.size() == 1) { // single exprent if (lastData.getAllPredecessorEdges().size() > 1) { // break edges issingle = true; } } boolean haslast = issingle || (lastDoExprent.type == Exprent.EXPRENT_ASSIGNMENT || lastDoExprent.type == Exprent.EXPRENT_FUNCTION); if (!haslast) { return false; } boolean hasinit = false; // search for an initializing exprent Statement current = stat; while (true) { Statement parent = current.getParent(); if (parent == null) { break; } if (parent.type == Statement.TYPE_SEQUENCE) { if (current == parent.getFirst()) { current = parent; } else { preData = current.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).get(0); preData = getLastDirectData(preData); if (preData != null && !preData.getExprents().isEmpty()) { initDoExprent = preData.getExprents().get(preData.getExprents().size() - 1); if (initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT) { hasinit = true; } } break; } } else { break; } } if ((hasinit && haslast) || issingle) { // FIXME: issingle sufficient? Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); set.remove(lastData); if (!set.isEmpty()) { return false; } stat.setLooptype(DoStatement.LOOP_FOR); if (hasinit) { stat.setInitExprent(preData.getExprents().remove(preData.getExprents().size() - 1)); } stat.setIncExprent(lastData.getExprents().remove(lastData.getExprents().size() - 1)); } if (lastData.getExprents().isEmpty()) { List<StatEdge> lst = lastData.getAllSuccessorEdges(); if (!lst.isEmpty()) { lastData.removeSuccessor(lst.get(0)); } removeLastEmptyStatement(stat, lastData); } return true; } private static void removeLastEmptyStatement(DoStatement dostat, Statement stat) { if (stat == dostat.getFirst()) { BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); bstat.setExprents(new ArrayList<>()); dostat.replaceStatement(stat, bstat); } else { for (StatEdge edge : stat.getAllPredecessorEdges()) { edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_CONTINUE); stat.removePredecessor(edge); edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, dostat); dostat.addPredecessor(edge); dostat.addLabeledEdge(edge); } // parent is a sequence statement stat.getParent().getStats().removeWithKey(stat.id); } } private static Statement getLastDirectData(Statement stat) { if (stat.getExprents() != null) { return stat; } switch (stat.type) { case Statement.TYPE_SEQUENCE: for (int i = stat.getStats().size() - 1; i >= 0; i--) { Statement tmp = getLastDirectData(stat.getStats().get(i)); if (tmp == null || !tmp.getExprents().isEmpty()) { return tmp; } } } return null; } }