summaryrefslogtreecommitdiffstats
path: root/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java
diff options
context:
space:
mode:
authorRoman Shevchenko <roman.shevchenko@jetbrains.com>2014-08-28 20:52:43 +0400
committerRoman Shevchenko <roman.shevchenko@jetbrains.com>2014-08-28 20:52:43 +0400
commit663631f0456fcc245dd835889f86541d75161c53 (patch)
treee183fa9777242e2900ff3648a726f05b190bc51b /src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java
parentf864084061806fda5510e50bfd2e69bf1dea406b (diff)
downloadfernflower-663631f0456fcc245dd835889f86541d75161c53.tar
fernflower-663631f0456fcc245dd835889f86541d75161c53.tar.gz
fernflower-663631f0456fcc245dd835889f86541d75161c53.tar.lz
fernflower-663631f0456fcc245dd835889f86541d75161c53.tar.xz
fernflower-663631f0456fcc245dd835889f86541d75161c53.zip
java-decompiler: post-import cleanup (classes moved)
Diffstat (limited to 'src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java')
-rw-r--r--src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java521
1 files changed, 521 insertions, 0 deletions
diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java
new file mode 100644
index 0000000..6d7a897
--- /dev/null
+++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java
@@ -0,0 +1,521 @@
+/*
+ * Fernflower - The Analytical Java Decompiler
+ * http://www.reversed-java.com
+ *
+ * (C) 2008 - 2010, Stiver
+ *
+ * This software is NEITHER public domain NOR free software
+ * as per GNU License. See license.txt for more details.
+ *
+ * This software is distributed WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE.
+ */
+
+package org.jetbrains.java.decompiler.modules.decompiler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
+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.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement;
+
+
+public class LabelHelper {
+
+
+ public static void cleanUpEdges(RootStatement root) {
+
+ resetAllEdges(root);
+
+ removeNonImmediateEdges(root);
+
+ liftClosures(root);
+
+ lowContinueLabels(root, new HashSet<StatEdge>());
+
+ lowClosures(root);
+
+ }
+
+ public static void identifyLabels(RootStatement root) {
+
+ setExplicitEdges(root);
+
+ hideDefaultSwitchEdges(root);
+
+ processStatementLabel(root);
+
+ setRetEdgesUnlabeled(root);
+ }
+
+ private static void liftClosures(Statement stat) {
+
+ for(StatEdge edge : stat.getAllSuccessorEdges()) {
+ switch(edge.getType()) {
+ case StatEdge.TYPE_CONTINUE:
+ if(edge.getDestination() != edge.closure) {
+ edge.getDestination().addLabeledEdge(edge);
+ }
+ break;
+ case StatEdge.TYPE_BREAK:
+ Statement dest = edge.getDestination();
+ if(dest.type != Statement.TYPE_DUMMYEXIT) {
+ Statement parent = dest.getParent();
+
+ List<Statement> lst = new ArrayList<Statement>();
+ if(parent.type == Statement.TYPE_SEQUENCE) {
+ lst.addAll(((SequenceStatement)parent).getStats());
+ } else if(parent.type == Statement.TYPE_SWITCH) {
+ lst.addAll(((SwitchStatement)parent).getCaseStatements());
+ }
+
+ for(int i=0;i<lst.size();i++) {
+ if(lst.get(i) == dest) {
+ lst.get(i-1).addLabeledEdge(edge);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for(Statement st : stat.getStats()) {
+ liftClosures(st);
+ }
+
+ }
+
+ private static void removeNonImmediateEdges(Statement stat) {
+
+ for(Statement st : stat.getStats()) {
+ removeNonImmediateEdges(st);
+ }
+
+ if(!stat.hasBasicSuccEdge()) {
+ for(StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_CONTINUE | StatEdge.TYPE_BREAK)) {
+ stat.removeSuccessor(edge);
+ }
+ }
+ }
+
+ public static void lowContinueLabels(Statement stat, HashSet<StatEdge> edges) {
+
+ boolean ok = (stat.type != Statement.TYPE_DO);
+ if(!ok) {
+ DoStatement dostat = (DoStatement)stat;
+ ok = dostat.getLooptype() == DoStatement.LOOP_DO ||
+ dostat.getLooptype() == DoStatement.LOOP_WHILE ||
+ (dostat.getLooptype() == DoStatement.LOOP_FOR && dostat.getIncExprent() == null);
+ }
+
+ if(ok) {
+ edges.addAll(stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE));
+ }
+
+ if(ok && stat.type == Statement.TYPE_DO) {
+ for(StatEdge edge: edges) {
+ if(stat.containsStatementStrict(edge.getSource())) {
+
+ edge.getDestination().removePredecessor(edge);
+ edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat);
+ stat.addPredecessor(edge);
+
+ stat.addLabeledEdge(edge);
+ }
+ }
+ }
+
+ for(Statement st: stat.getStats()) {
+ if(st == stat.getFirst()) {
+ lowContinueLabels(st, edges);
+ } else {
+ lowContinueLabels(st, new HashSet<StatEdge>());
+ }
+ }
+ }
+
+ public static void lowClosures(Statement stat) {
+
+ for(StatEdge edge : new ArrayList<StatEdge>(stat.getLabelEdges())) {
+
+ if(edge.getType() == StatEdge.TYPE_BREAK) { // FIXME: ?
+ for(Statement st : stat.getStats()) {
+ if(st.containsStatementStrict(edge.getSource())) {
+ if(MergeHelper.isDirectPath(st, edge.getDestination())) {
+ st.addLabeledEdge(edge);
+ }
+ }
+ }
+ }
+ }
+
+ for(Statement st: stat.getStats()) {
+ lowClosures(st);
+ }
+
+ }
+
+ private static void resetAllEdges(Statement stat) {
+
+ for(Statement st: stat.getStats()) {
+ resetAllEdges(st);
+ }
+
+ for(StatEdge edge: stat.getAllSuccessorEdges()) {
+ edge.explicit = true;
+ edge.labeled = true;
+ }
+ }
+
+ private static void setRetEdgesUnlabeled(RootStatement root) {
+ Statement exit = root.getDummyExit();
+ for(StatEdge edge: exit.getAllPredecessorEdges()) {
+ List<Exprent> lst = edge.getSource().getExprents();
+ if(edge.getType() == StatEdge.TYPE_FINALLYEXIT || (lst != null && !lst.isEmpty() &&
+ lst.get(lst.size()-1).type == Exprent.EXPRENT_EXIT)) {
+ edge.labeled = false;
+ }
+ }
+ }
+
+ private static HashMap<Statement, List<StatEdge>> setExplicitEdges(Statement stat) {
+
+ HashMap<Statement, List<StatEdge>> mapEdges = new HashMap<Statement, List<StatEdge>>();
+
+ if(stat.getExprents() != null) {
+ return mapEdges;
+ }
+
+
+ switch(stat.type) {
+ case Statement.TYPE_TRYCATCH:
+ case Statement.TYPE_CATCHALL:
+
+ for(Statement st : stat.getStats()) {
+ HashMap<Statement, List<StatEdge>> mapEdges1 = setExplicitEdges(st);
+ processEdgesWithNext(st, mapEdges1, null);
+
+ if(stat.type == Statement.TYPE_TRYCATCH || st == stat.getFirst()) { // edges leaving a finally catch block are always explicit
+ // merge the maps
+ if(mapEdges1 != null) {
+ for(Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) {
+ if(mapEdges.containsKey(entr.getKey())) {
+ mapEdges.get(entr.getKey()).addAll(entr.getValue());
+ } else {
+ mapEdges.put(entr.getKey(), entr.getValue());
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ case Statement.TYPE_DO:
+ mapEdges = setExplicitEdges(stat.getFirst());
+ processEdgesWithNext(stat.getFirst(), mapEdges, stat);
+ break;
+ case Statement.TYPE_IF:
+ IfStatement ifstat = (IfStatement)stat;
+ // head statement is a basic block
+ if(ifstat.getIfstat() == null) { // empty if
+ processEdgesWithNext(ifstat.getFirst(), mapEdges, null);
+ } else {
+ if(ifstat.getIfstat() != null) {
+ mapEdges = setExplicitEdges(ifstat.getIfstat());
+ processEdgesWithNext(ifstat.getIfstat(), mapEdges, null);
+ }
+
+ HashMap<Statement, List<StatEdge>> mapEdges1 = null;
+ if(ifstat.getElsestat() != null) {
+ mapEdges1 = setExplicitEdges(ifstat.getElsestat());
+ processEdgesWithNext(ifstat.getElsestat(), mapEdges1, null);
+ }
+
+ // merge the maps
+ if(mapEdges1 != null) {
+ for(Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) {
+ if(mapEdges.containsKey(entr.getKey())) {
+ mapEdges.get(entr.getKey()).addAll(entr.getValue());
+ } else {
+ mapEdges.put(entr.getKey(), entr.getValue());
+ }
+ }
+ }
+ }
+ break;
+ case Statement.TYPE_ROOT:
+ mapEdges = setExplicitEdges(stat.getFirst());
+ processEdgesWithNext(stat.getFirst(), mapEdges, ((RootStatement)stat).getDummyExit());
+ break;
+ case Statement.TYPE_SEQUENCE:
+ int index = 0;
+ while(index < stat.getStats().size()-1) {
+ Statement st = stat.getStats().get(index);
+ processEdgesWithNext(st, setExplicitEdges(st), stat.getStats().get(index+1));
+ index++;
+ }
+
+ Statement st = stat.getStats().get(index);
+ mapEdges = setExplicitEdges(st);
+ processEdgesWithNext(st, mapEdges, null);
+ break;
+ case Statement.TYPE_SWITCH:
+ SwitchStatement swst = (SwitchStatement)stat;
+
+ for(int i=0;i<swst.getCaseStatements().size()-1;i++) {
+ Statement stt = swst.getCaseStatements().get(i);
+ Statement stnext = swst.getCaseStatements().get(i+1);
+
+ if(stnext.getExprents() != null && stnext.getExprents().isEmpty()) {
+ stnext = stnext.getAllSuccessorEdges().get(0).getDestination();
+ }
+ processEdgesWithNext(stt, setExplicitEdges(stt), stnext);
+ }
+
+ int last = swst.getCaseStatements().size()-1;
+ if(last >= 0) { // empty switch possible
+ Statement stlast = swst.getCaseStatements().get(last);
+ if(stlast.getExprents() != null && stlast.getExprents().isEmpty()) {
+ StatEdge edge = stlast.getAllSuccessorEdges().get(0);
+ mapEdges.put(edge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {edge})));
+ } else {
+ mapEdges = setExplicitEdges(stlast);
+ processEdgesWithNext(stlast, mapEdges, null);
+ }
+ }
+
+ break;
+ case Statement.TYPE_SYNCRONIZED:
+ SynchronizedStatement synstat = (SynchronizedStatement)stat;
+
+ processEdgesWithNext(synstat.getFirst(), setExplicitEdges(stat.getFirst()), synstat.getBody()); // FIXME: basic block?
+ mapEdges = setExplicitEdges(synstat.getBody());
+ processEdgesWithNext(synstat.getBody(), mapEdges, null);
+ }
+
+
+ return mapEdges;
+ }
+
+ private static void processEdgesWithNext(Statement stat, HashMap<Statement, List<StatEdge>> mapEdges, Statement next) {
+
+ StatEdge statedge = null;
+
+ List<StatEdge> lstSuccs = stat.getAllSuccessorEdges();
+ if(!lstSuccs.isEmpty()) {
+ statedge = lstSuccs.get(0);
+
+ if(statedge.getDestination() == next) {
+ statedge.explicit = false;
+ statedge = null;
+ } else {
+ next = statedge.getDestination();
+ }
+ }
+
+ // no next for a do statement
+ if(stat.type == Statement.TYPE_DO && ((DoStatement)stat).getLooptype() == DoStatement.LOOP_DO) {
+ next = null;
+ }
+
+ if(next == null) {
+ if(mapEdges.size() == 1) {
+ List<StatEdge> lstEdges = mapEdges.values().iterator().next();
+ if(lstEdges.size() > 1 && mapEdges.keySet().iterator().next().type != Statement.TYPE_DUMMYEXIT) {
+ StatEdge edge_example = lstEdges.get(0);
+
+ Statement closure = stat.getParent();
+ if(!closure.containsStatementStrict(edge_example.closure)) {
+ closure = edge_example.closure;
+ }
+
+ StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure);
+ stat.addSuccessor(newedge);
+
+ for(StatEdge edge : lstEdges) {
+ edge.explicit = false;
+ }
+
+ mapEdges.put(newedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {newedge})));
+ }
+ }
+ } else {
+
+ boolean implfound = false;
+
+ for(Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) {
+ if(entr.getKey() == next) {
+ for(StatEdge edge : entr.getValue()) {
+ edge.explicit = false;
+ }
+ implfound = true;
+ break;
+ }
+ }
+
+ if(stat.getAllSuccessorEdges().isEmpty() && !implfound) {
+ List<StatEdge> lstEdges = null;
+ for(Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) {
+ if(entr.getKey().type != Statement.TYPE_DUMMYEXIT &&
+ (lstEdges == null || entr.getValue().size() > lstEdges.size())) {
+ lstEdges = entr.getValue();
+ }
+ }
+
+ if(lstEdges != null && lstEdges.size() > 1) {
+ StatEdge edge_example = lstEdges.get(0);
+
+ Statement closure = stat.getParent();
+ if(!closure.containsStatementStrict(edge_example.closure)) {
+ closure = edge_example.closure;
+ }
+
+ StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure);
+ stat.addSuccessor(newedge);
+
+ for(StatEdge edge : lstEdges) {
+ edge.explicit = false;
+ }
+ }
+ }
+
+ mapEdges.clear();
+ }
+
+ if(statedge != null) {
+ mapEdges.put(statedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {statedge})));
+ }
+
+ }
+
+ private static void hideDefaultSwitchEdges(Statement stat) {
+
+ if(stat.type == Statement.TYPE_SWITCH) {
+ SwitchStatement swst = (SwitchStatement)stat;
+
+ int last = swst.getCaseStatements().size()-1;
+ if(last >= 0) { // empty switch possible
+ Statement stlast = swst.getCaseStatements().get(last);
+
+ if(stlast.getExprents() != null && stlast.getExprents().isEmpty()) {
+ if(!stlast.getAllSuccessorEdges().get(0).explicit) {
+ List<StatEdge> lstEdges = swst.getCaseEdges().get(last);
+ lstEdges.remove(swst.getDefault_edge());
+
+ if(lstEdges.isEmpty()) {
+ swst.getCaseStatements().remove(last);
+ swst.getCaseEdges().remove(last);
+ }
+ }
+ }
+ }
+ }
+
+ for(Statement st : stat.getStats()) {
+ hideDefaultSwitchEdges(st);
+ }
+
+ }
+
+ private static HashSet<Statement>[] processStatementLabel(Statement stat) {
+
+ HashSet<Statement> setBreak = new HashSet<Statement>();
+ HashSet<Statement> setContinue = new HashSet<Statement>();
+
+ if(stat.getExprents() == null) {
+ for(Statement st: stat.getStats()) {
+ HashSet<Statement>[] arr = processStatementLabel(st);
+
+ setBreak.addAll(arr[0]);
+ setContinue.addAll(arr[1]);
+ }
+
+ boolean shieldtype = (stat.type == Statement.TYPE_DO || stat.type == Statement.TYPE_SWITCH);
+
+ for(StatEdge edge: stat.getLabelEdges()) {
+ if(edge.explicit) {
+ if(shieldtype && ((edge.getType() == StatEdge.TYPE_BREAK && setBreak.contains(edge.getSource())) ||
+ (edge.getType() == StatEdge.TYPE_CONTINUE && setContinue.contains(edge.getSource())))) {
+ edge.labeled = false;
+ }
+ }
+ }
+
+ switch(stat.type) {
+ case Statement.TYPE_DO:
+ setContinue.clear();
+ case Statement.TYPE_SWITCH:
+ setBreak.clear();
+ }
+ }
+
+ setBreak.add(stat);
+ setContinue.add(stat);
+
+ return new HashSet[] {setBreak, setContinue};
+ }
+
+ public static void replaceContinueWithBreak(Statement stat) {
+
+ if(stat.type == Statement.TYPE_DO) {
+
+ List<StatEdge> lst = stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE);
+
+ for(StatEdge edge : lst) {
+
+ if(edge.explicit) {
+ Statement minclosure = getMinContinueClosure(edge);
+
+ if(minclosure != edge.closure &&
+ !InlineSingleBlockHelper.isBreakEdgeLabeled(edge.getSource(), minclosure)) {
+ edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK);
+ edge.labeled = false;
+ minclosure.addLabeledEdge(edge);
+ }
+ }
+ }
+ }
+
+ for(Statement st : stat.getStats()) {
+ replaceContinueWithBreak(st);
+ }
+
+ }
+
+ private static Statement getMinContinueClosure(StatEdge edge) {
+
+ Statement closure = edge.closure;
+ for(;;) {
+
+ boolean found = false;
+
+ for(Statement st : closure.getStats()) {
+ if(st.containsStatementStrict(edge.getSource())) {
+ if(MergeHelper.isDirectPath(st, edge.getDestination())) {
+ closure = st;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if(!found) {
+ break;
+ }
+ }
+
+ return closure;
+ }
+
+}