summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java')
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java574
1 files changed, 574 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java
new file mode 100644
index 000000000000..05d2f33b27e5
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java
@@ -0,0 +1,574 @@
+/*
+ * 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.sforms;
+
+import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+
+public class FlattenStatementsHelper {
+
+ // statement.id, node.id(direct), node.id(continue)
+ private Map<Integer, String[]> mapDestinationNodes = new HashMap<Integer, String[]>();
+
+ // node.id(source), statement.id(destination), edge type
+ private List<Edge> listEdges = new ArrayList<Edge>();
+
+ // node.id(exit), [node.id(source), statement.id(destination)]
+ private Map<String, List<String[]>> mapShortRangeFinallyPathIds = new HashMap<String, List<String[]>>();
+
+ // node.id(exit), [node.id(source), statement.id(destination)]
+ private Map<String, List<String[]>> mapLongRangeFinallyPathIds = new HashMap<String, List<String[]>>();
+
+ // positive if branches
+ private Map<String, Integer> mapPosIfBranch = new HashMap<String, Integer>();
+
+ private DirectGraph graph;
+
+ private RootStatement root;
+
+ public DirectGraph buildDirectGraph(RootStatement root) {
+
+ this.root = root;
+
+ graph = new DirectGraph();
+
+ flattenStatement();
+
+ // dummy exit node
+ Statement dummyexit = root.getDummyExit();
+ DirectNode node = new DirectNode(DirectNode.NODE_DIRECT, dummyexit, dummyexit.id.toString());
+ node.exprents = new ArrayList<Exprent>();
+ graph.nodes.addWithKey(node, node.id);
+ mapDestinationNodes.put(dummyexit.id, new String[]{node.id, null});
+
+ setEdges();
+
+ graph.first = graph.nodes.getWithKey(mapDestinationNodes.get(root.id)[0]);
+ graph.sortReversePostOrder();
+
+ return graph;
+ }
+
+ private void flattenStatement() {
+
+ class StatementStackEntry {
+ public Statement statement;
+ public LinkedList<StackEntry> stackFinally;
+ public List<Exprent> tailExprents;
+
+ public int statementIndex;
+ public int edgeIndex;
+ public List<StatEdge> succEdges;
+
+ public StatementStackEntry(Statement statement, LinkedList<StackEntry> stackFinally, List<Exprent> tailExprents) {
+ this.statement = statement;
+ this.stackFinally = stackFinally;
+ this.tailExprents = tailExprents;
+ }
+ }
+
+ LinkedList<StatementStackEntry> lstStackStatements = new LinkedList<StatementStackEntry>();
+
+ lstStackStatements.add(new StatementStackEntry(root, new LinkedList<StackEntry>(), null));
+
+ mainloop:
+ while (!lstStackStatements.isEmpty()) {
+
+ StatementStackEntry statEntry = lstStackStatements.removeFirst();
+
+ Statement stat = statEntry.statement;
+ LinkedList<StackEntry> stackFinally = statEntry.stackFinally;
+ int statementBreakIndex = statEntry.statementIndex;
+
+ DirectNode node, nd;
+
+ List<StatEdge> lstSuccEdges = new ArrayList<StatEdge>();
+ DirectNode sourcenode = null;
+
+ if (statEntry.succEdges == null) {
+
+ switch (stat.type) {
+ case Statement.TYPE_BASICBLOCK:
+ node = new DirectNode(DirectNode.NODE_DIRECT, stat, (BasicBlockStatement)stat);
+ if (stat.getExprents() != null) {
+ node.exprents = stat.getExprents();
+ }
+ graph.nodes.putWithKey(node, node.id);
+ mapDestinationNodes.put(stat.id, new String[]{node.id, null});
+
+ lstSuccEdges.addAll(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL));
+ sourcenode = node;
+
+ List<Exprent> tailExprentList = statEntry.tailExprents;
+
+ if (tailExprentList != null) {
+ DirectNode tail = new DirectNode(DirectNode.NODE_TAIL, stat, stat.id + "_tail");
+ tail.exprents = tailExprentList;
+ graph.nodes.putWithKey(tail, tail.id);
+
+ mapDestinationNodes.put(-stat.id, new String[]{tail.id, null});
+ listEdges.add(new Edge(node.id, -stat.id, StatEdge.TYPE_REGULAR));
+
+ sourcenode = tail;
+ }
+
+ // 'if' statement: record positive branch
+ if (stat.getLastBasicType() == Statement.LASTBASICTYPE_IF) {
+ mapPosIfBranch.put(sourcenode.id, lstSuccEdges.get(0).getDestination().id);
+ }
+
+ break;
+ case Statement.TYPE_CATCHALL:
+ case Statement.TYPE_TRYCATCH:
+ DirectNode firstnd = new DirectNode(DirectNode.NODE_TRY, stat, stat.id + "_try");
+
+ mapDestinationNodes.put(stat.id, new String[]{firstnd.id, null});
+ graph.nodes.putWithKey(firstnd, firstnd.id);
+
+ LinkedList<StatementStackEntry> lst = new LinkedList<StatementStackEntry>();
+
+ for (Statement st : stat.getStats()) {
+ listEdges.add(new Edge(firstnd.id, st.id, StatEdge.TYPE_REGULAR));
+
+ LinkedList<StackEntry> stack = stackFinally;
+ if (stat.type == Statement.TYPE_CATCHALL && ((CatchAllStatement)stat).isFinally()) {
+ stack = new LinkedList<StackEntry>(stackFinally);
+
+ if (st == stat.getFirst()) { // catch head
+ stack.add(new StackEntry((CatchAllStatement)stat, Boolean.FALSE));
+ }
+ else { // handler
+ stack.add(new StackEntry((CatchAllStatement)stat, Boolean.TRUE, StatEdge.TYPE_BREAK,
+ root.getDummyExit(), st, st, firstnd, firstnd, true));
+ }
+ }
+ lst.add(new StatementStackEntry(st, stack, null));
+ }
+
+ lstStackStatements.addAll(0, lst);
+ break;
+ case Statement.TYPE_DO:
+ if (statementBreakIndex == 0) {
+ statEntry.statementIndex = 1;
+ lstStackStatements.addFirst(statEntry);
+ lstStackStatements.addFirst(new StatementStackEntry(stat.getFirst(), stackFinally, null));
+
+ continue mainloop;
+ }
+
+ nd = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]);
+
+ DoStatement dostat = (DoStatement)stat;
+ int looptype = dostat.getLooptype();
+
+ if (looptype == DoStatement.LOOP_DO) {
+ mapDestinationNodes.put(stat.id, new String[]{nd.id, nd.id});
+ break;
+ }
+
+ lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge
+
+ switch (looptype) {
+ case DoStatement.LOOP_WHILE:
+ case DoStatement.LOOP_DOWHILE:
+ node = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id + "_cond");
+ node.exprents = dostat.getConditionExprentList();
+ graph.nodes.putWithKey(node, node.id);
+
+ listEdges.add(new Edge(node.id, stat.getFirst().id, StatEdge.TYPE_REGULAR));
+
+ if (looptype == DoStatement.LOOP_WHILE) {
+ mapDestinationNodes.put(stat.id, new String[]{node.id, node.id});
+ }
+ else {
+ mapDestinationNodes.put(stat.id, new String[]{nd.id, node.id});
+
+ boolean found = false;
+ for (Edge edge : listEdges) {
+ if (edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE));
+ }
+ }
+ sourcenode = node;
+ break;
+ case DoStatement.LOOP_FOR:
+ DirectNode nodeinit = new DirectNode(DirectNode.NODE_INIT, stat, stat.id + "_init");
+ if (dostat.getInitExprent() != null) {
+ nodeinit.exprents = dostat.getInitExprentList();
+ }
+ graph.nodes.putWithKey(nodeinit, nodeinit.id);
+
+ DirectNode nodecond = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id + "_cond");
+ nodecond.exprents = dostat.getConditionExprentList();
+ graph.nodes.putWithKey(nodecond, nodecond.id);
+
+ DirectNode nodeinc = new DirectNode(DirectNode.NODE_INCREMENT, stat, stat.id + "_inc");
+ nodeinc.exprents = dostat.getIncExprentList();
+ graph.nodes.putWithKey(nodeinc, nodeinc.id);
+
+ mapDestinationNodes.put(stat.id, new String[]{nodeinit.id, nodeinc.id});
+ mapDestinationNodes.put(-stat.id, new String[]{nodecond.id, null});
+
+ listEdges.add(new Edge(nodecond.id, stat.getFirst().id, StatEdge.TYPE_REGULAR));
+ listEdges.add(new Edge(nodeinit.id, -stat.id, StatEdge.TYPE_REGULAR));
+ listEdges.add(new Edge(nodeinc.id, -stat.id, StatEdge.TYPE_REGULAR));
+
+ boolean found = false;
+ for (Edge edge : listEdges) {
+ if (edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE));
+ }
+
+ sourcenode = nodecond;
+ }
+ break;
+ case Statement.TYPE_SYNCRONIZED:
+ case Statement.TYPE_SWITCH:
+ case Statement.TYPE_IF:
+ case Statement.TYPE_SEQUENCE:
+ case Statement.TYPE_ROOT:
+ int statsize = stat.getStats().size();
+ if (stat.type == Statement.TYPE_SYNCRONIZED) {
+ statsize = 2; // exclude the handler if synchronized
+ }
+
+ if (statementBreakIndex <= statsize) {
+ List<Exprent> tailexprlst = null;
+
+ switch (stat.type) {
+ case Statement.TYPE_SYNCRONIZED:
+ tailexprlst = ((SynchronizedStatement)stat).getHeadexprentList();
+ break;
+ case Statement.TYPE_SWITCH:
+ tailexprlst = ((SwitchStatement)stat).getHeadexprentList();
+ break;
+ case Statement.TYPE_IF:
+ tailexprlst = ((IfStatement)stat).getHeadexprentList();
+ }
+
+ for (int i = statementBreakIndex; i < statsize; i++) {
+ statEntry.statementIndex = i + 1;
+ lstStackStatements.addFirst(statEntry);
+ lstStackStatements.addFirst(
+ new StatementStackEntry(stat.getStats().get(i), stackFinally,
+ (i == 0 && tailexprlst != null && tailexprlst.get(0) != null) ? tailexprlst : null));
+
+ continue mainloop;
+ }
+
+ node = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]);
+ mapDestinationNodes.put(stat.id, new String[]{node.id, null});
+
+ if (stat.type == Statement.TYPE_IF && ((IfStatement)stat).iftype == IfStatement.IFTYPE_IF) {
+ lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge
+ sourcenode = tailexprlst.get(0) == null ? node : graph.nodes.getWithKey(node.id + "_tail");
+ }
+ }
+ }
+ }
+
+ // no successor edges
+ if (sourcenode != null) {
+
+ if (statEntry.succEdges != null) {
+ lstSuccEdges = statEntry.succEdges;
+ }
+
+ for (int edgeindex = statEntry.edgeIndex; edgeindex < lstSuccEdges.size(); edgeindex++) {
+
+ StatEdge edge = lstSuccEdges.get(edgeindex);
+
+ LinkedList<StackEntry> stack = new LinkedList<StackEntry>(stackFinally);
+
+ int edgetype = edge.getType();
+ Statement destination = edge.getDestination();
+
+ DirectNode finallyShortRangeSource = sourcenode;
+ DirectNode finallyLongRangeSource = sourcenode;
+ Statement finallyShortRangeEntry = null;
+ Statement finallyLongRangeEntry = null;
+
+ boolean isFinallyMonitorExceptionPath = false;
+
+ boolean isFinallyExit = false;
+
+ while (true) {
+
+ StackEntry entry = null;
+ if (!stack.isEmpty()) {
+ entry = stack.getLast();
+ }
+
+ boolean created = true;
+
+ if (entry == null) {
+ saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,
+ finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);
+ }
+ else {
+
+ CatchAllStatement catchall = entry.catchstatement;
+
+ if (entry.state) { // finally handler statement
+ if (edgetype == StatEdge.TYPE_FINALLYEXIT) {
+
+ stack.removeLast();
+ destination = entry.destination;
+ edgetype = entry.edgetype;
+
+ finallyShortRangeSource = entry.finallyShortRangeSource;
+ finallyLongRangeSource = entry.finallyLongRangeSource;
+ finallyShortRangeEntry = entry.finallyShortRangeEntry;
+ finallyLongRangeEntry = entry.finallyLongRangeEntry;
+
+ isFinallyExit = true;
+ isFinallyMonitorExceptionPath = (catchall.getMonitor() != null) & entry.isFinallyExceptionPath;
+
+ created = false;
+ }
+ else {
+ if (!catchall.containsStatementStrict(destination)) {
+ stack.removeLast();
+ created = false;
+ }
+ else {
+ saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,
+ finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);
+ }
+ }
+ }
+ else { // finally protected try statement
+ if (!catchall.containsStatementStrict(destination)) {
+ saveEdge(sourcenode, catchall.getHandler(), StatEdge.TYPE_REGULAR, isFinallyExit ? finallyShortRangeSource : null,
+ finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);
+
+ stack.removeLast();
+ stack.add(new StackEntry(catchall, Boolean.TRUE, edgetype, destination, catchall.getHandler(),
+ finallyLongRangeEntry == null ? catchall.getHandler() : finallyLongRangeEntry,
+ sourcenode, finallyLongRangeSource, false));
+
+ statEntry.edgeIndex = edgeindex + 1;
+ statEntry.succEdges = lstSuccEdges;
+ lstStackStatements.addFirst(statEntry);
+ lstStackStatements.addFirst(new StatementStackEntry(catchall.getHandler(), stack, null));
+
+ continue mainloop;
+ }
+ else {
+ saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,
+ finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);
+ }
+ }
+ }
+
+ if (created) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void saveEdge(DirectNode sourcenode,
+ Statement destination,
+ int edgetype,
+ DirectNode finallyShortRangeSource,
+ DirectNode finallyLongRangeSource,
+ Statement finallyShortRangeEntry,
+ Statement finallyLongRangeEntry,
+ boolean isFinallyMonitorExceptionPath) {
+
+ if (edgetype != StatEdge.TYPE_FINALLYEXIT) {
+ listEdges.add(new Edge(sourcenode.id, destination.id, edgetype));
+ }
+
+ if (finallyShortRangeSource != null) {
+
+ boolean isContinueEdge = (edgetype == StatEdge.TYPE_CONTINUE);
+
+ List<String[]> lst = mapShortRangeFinallyPathIds.get(sourcenode.id);
+ if (lst == null) {
+ mapShortRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>());
+ }
+ lst.add(new String[]{finallyShortRangeSource.id, destination.id.toString(), finallyShortRangeEntry.id.toString(),
+ isFinallyMonitorExceptionPath ? "1" : null, isContinueEdge ? "1" : null});
+
+ lst = mapLongRangeFinallyPathIds.get(sourcenode.id);
+ if (lst == null) {
+ mapLongRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>());
+ }
+ lst.add(new String[]{finallyLongRangeSource.id, destination.id.toString(), finallyLongRangeEntry.id.toString(),
+ isContinueEdge ? "1" : null});
+ }
+ }
+
+ private void setEdges() {
+
+ for (Edge edge : listEdges) {
+
+ String sourceid = edge.sourceid;
+ Integer statid = edge.statid;
+
+ DirectNode source = graph.nodes.getWithKey(sourceid);
+
+ DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(statid)[edge.edgetype == StatEdge.TYPE_CONTINUE ? 1 : 0]);
+
+ if (!source.succs.contains(dest)) {
+ source.succs.add(dest);
+ }
+
+ if (!dest.preds.contains(source)) {
+ dest.preds.add(source);
+ }
+
+ if (mapPosIfBranch.containsKey(sourceid) && !statid.equals(mapPosIfBranch.get(sourceid))) {
+ graph.mapNegIfBranch.put(sourceid, dest.id);
+ }
+ }
+
+ for (int i = 0; i < 2; i++) {
+ for (Entry<String, List<String[]>> ent : (i == 0 ? mapShortRangeFinallyPathIds : mapLongRangeFinallyPathIds).entrySet()) {
+
+ List<FinallyPathWrapper> newLst = new ArrayList<FinallyPathWrapper>();
+
+ List<String[]> lst = ent.getValue();
+ for (String[] arr : lst) {
+
+ boolean isContinueEdge = arr[i == 0 ? 4 : 3] != null;
+
+ DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[1]))[isContinueEdge ? 1 : 0]);
+ DirectNode enter = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[2]))[0]);
+
+ newLst.add(new FinallyPathWrapper(arr[0], dest.id, enter.id));
+
+ if (i == 0 && arr[3] != null) {
+ graph.mapFinallyMonitorExceptionPathExits.put(ent.getKey(), dest.id);
+ }
+ }
+
+ if (!newLst.isEmpty()) {
+ (i == 0 ? graph.mapShortRangeFinallyPaths : graph.mapLongRangeFinallyPaths).put(ent.getKey(),
+ new ArrayList<FinallyPathWrapper>(
+ new HashSet<FinallyPathWrapper>(newLst)));
+ }
+ }
+ }
+ }
+
+ public Map<Integer, String[]> getMapDestinationNodes() {
+ return mapDestinationNodes;
+ }
+
+ public static class FinallyPathWrapper {
+ public String source;
+ public String destination;
+ public String entry;
+
+ private FinallyPathWrapper(String source, String destination, String entry) {
+ this.source = source;
+ this.destination = destination;
+ this.entry = entry;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof FinallyPathWrapper)) return false;
+
+ FinallyPathWrapper fpw = (FinallyPathWrapper)o;
+ return (source + ":" + destination + ":" + entry).equals(fpw.source + ":" + fpw.destination + ":" + fpw.entry);
+ }
+
+ @Override
+ public int hashCode() {
+ return (source + ":" + destination + ":" + entry).hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return source + "->(" + entry + ")->" + destination;
+ }
+ }
+
+
+ private static class StackEntry {
+
+ public CatchAllStatement catchstatement;
+ public boolean state;
+ public int edgetype;
+ public boolean isFinallyExceptionPath;
+
+ public Statement destination;
+ public Statement finallyShortRangeEntry;
+ public Statement finallyLongRangeEntry;
+ public DirectNode finallyShortRangeSource;
+ public DirectNode finallyLongRangeSource;
+
+ public StackEntry(CatchAllStatement catchstatement,
+ boolean state,
+ int edgetype,
+ Statement destination,
+ Statement finallyShortRangeEntry,
+ Statement finallyLongRangeEntry,
+ DirectNode finallyShortRangeSource,
+ DirectNode finallyLongRangeSource,
+ boolean isFinallyExceptionPath) {
+
+ this.catchstatement = catchstatement;
+ this.state = state;
+ this.edgetype = edgetype;
+ this.isFinallyExceptionPath = isFinallyExceptionPath;
+
+ this.destination = destination;
+ this.finallyShortRangeEntry = finallyShortRangeEntry;
+ this.finallyLongRangeEntry = finallyLongRangeEntry;
+ this.finallyShortRangeSource = finallyShortRangeSource;
+ this.finallyLongRangeSource = finallyLongRangeSource;
+ }
+
+ public StackEntry(CatchAllStatement catchstatement, boolean state) {
+ this(catchstatement, state, -1, null, null, null, null, null, false);
+ }
+ }
+
+ private static class Edge {
+ public String sourceid;
+ public Integer statid;
+ public int edgetype;
+
+ public Edge(String sourceid, Integer statid, int edgetype) {
+ this.sourceid = sourceid;
+ this.statid = statid;
+ this.edgetype = edgetype;
+ }
+ }
+}