diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java new file mode 100644 index 000000000000..71d3d7991287 --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -0,0 +1,421 @@ +/* + * 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<Exprent>()); + 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<Exprent>()); + + 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<Exprent>()); + 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; + } +} |