summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java
diff options
context:
space:
mode:
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.java421
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;
+ }
+}