diff options
author | Roman Shevchenko <roman.shevchenko@jetbrains.com> | 2014-08-28 20:52:43 +0400 |
---|---|---|
committer | Roman Shevchenko <roman.shevchenko@jetbrains.com> | 2014-08-28 20:52:43 +0400 |
commit | 663631f0456fcc245dd835889f86541d75161c53 (patch) | |
tree | e183fa9777242e2900ff3648a726f05b190bc51b /src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java | |
parent | f864084061806fda5510e50bfd2e69bf1dea406b (diff) | |
download | fernflower-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.java | 521 |
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; + } + +} |