diff options
Diffstat (limited to 'src/de/fernflower/modules/decompiler/SequenceHelper.java')
-rw-r--r-- | src/de/fernflower/modules/decompiler/SequenceHelper.java | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/src/de/fernflower/modules/decompiler/SequenceHelper.java b/src/de/fernflower/modules/decompiler/SequenceHelper.java new file mode 100644 index 0000000..6a27902 --- /dev/null +++ b/src/de/fernflower/modules/decompiler/SequenceHelper.java @@ -0,0 +1,326 @@ +/* + * 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 de.fernflower.modules.decompiler; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import de.fernflower.code.cfg.BasicBlock; +import de.fernflower.main.DecompilerContext; +import de.fernflower.main.collectors.CounterContainer; +import de.fernflower.modules.decompiler.exps.Exprent; +import de.fernflower.modules.decompiler.stats.BasicBlockStatement; +import de.fernflower.modules.decompiler.stats.SequenceStatement; +import de.fernflower.modules.decompiler.stats.Statement; + + +public class SequenceHelper { + + + public static void condenseSequences(Statement root) { + condenseSequencesRec(root); + } + + private static void condenseSequencesRec(Statement stat) { + + if(stat.type == Statement.TYPE_SEQUENCE) { + + List<Statement> lst = new ArrayList<Statement>(); + lst.addAll(stat.getStats()); + + boolean unfolded = false; + + // unfold blocks + for(int i=0;i<lst.size();i++) { + Statement st = lst.get(i); + if(st.type == Statement.TYPE_SEQUENCE) { + + removeEmptyStatements((SequenceStatement)st); + + if(i == lst.size()-1 || isSequenceDisbandable(st, lst.get(i+1))) { + // move predecessors + Statement first = st.getFirst(); + for(StatEdge edge: st.getAllPredecessorEdges()) { + st.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, first); + first.addPredecessor(edge); + } + + // move successors + Statement last = st.getStats().getLast(); + if(last.getAllSuccessorEdges().isEmpty() && i<lst.size()-1) { + last.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, last, lst.get(i+1))); + } else { + for(StatEdge edge: last.getAllSuccessorEdges()) { + if(i == lst.size()-1) { + if(edge.closure == st) { + stat.addLabeledEdge(edge); + } + } else { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR); + edge.closure.getLabelEdges().remove(edge); + edge.closure = null; + } + } + } + + for(StatEdge edge : st.getAllSuccessorEdges()) { + st.removeSuccessor(edge); + } + + for(StatEdge edge: new HashSet<StatEdge>(st.getLabelEdges())) { + if(edge.getSource() != last) { + last.addLabeledEdge(edge); + } + } + + lst.remove(i); + lst.addAll(i, st.getStats()); + i--; + + unfolded = true; + } + } + } + + if(unfolded) { + SequenceStatement sequence = new SequenceStatement(lst); + sequence.setAllParent(); + + stat.getParent().replaceStatement(stat, sequence); + + stat = sequence; + } + } + + // sequence consisting of one statement -> disband + if(stat.type == Statement.TYPE_SEQUENCE) { + + removeEmptyStatements((SequenceStatement)stat); + + if(stat.getStats().size() == 1) { + + Statement st = stat.getFirst(); + + boolean ok = st.getAllSuccessorEdges().isEmpty(); + if(!ok) { + StatEdge edge = st.getAllSuccessorEdges().get(0); + + ok = stat.getAllSuccessorEdges().isEmpty(); + if(!ok) { + StatEdge statedge = stat.getAllSuccessorEdges().get(0); + ok = (edge.getDestination() == statedge.getDestination()); + + if(ok) { + st.removeSuccessor(edge); + } + } + } + + if(ok) { + stat.getParent().replaceStatement(stat, st); + stat = st; + } + } + } + + // replace flat statements with synthetic basic blocks + outer: + for(;;) { + for(Statement st: stat.getStats()) { + if((st.getStats().isEmpty() || st.getExprents() != null) && st.type != Statement.TYPE_BASICBLOCK) { + destroyAndFlattenStatement(st); + continue outer; + } + } + break; + } + + // recursion + for(int i=0;i<stat.getStats().size();i++) { + condenseSequencesRec(stat.getStats().get(i)); + } + + } + + private static boolean isSequenceDisbandable(Statement block, Statement next) { + + Statement last = block.getStats().getLast(); + List<StatEdge> lstSuccs = last.getAllSuccessorEdges(); + if(!lstSuccs.isEmpty()) { + if(lstSuccs.get(0).getDestination() != next) { + return false; + } + } + + for(StatEdge edge : next.getPredecessorEdges(StatEdge.TYPE_BREAK)) { + if(last != edge.getSource() && !last.containsStatementStrict(edge.getSource())) { + return false; + } + } + + return true; + } + + private static void removeEmptyStatements(SequenceStatement sequence) { + + if(sequence.getStats().size() <= 1) { + return; + } + + mergeFlatStatements(sequence); + + for(;;) { + + boolean found = false; + + for(Statement st: sequence.getStats()) { + + if(st.getExprents() != null && st.getExprents().isEmpty()) { + + if(st.getAllSuccessorEdges().isEmpty()) { + List<StatEdge> lstBreaks = st.getPredecessorEdges(StatEdge.TYPE_BREAK); + + if(lstBreaks.isEmpty()) { + for(StatEdge edge: st.getAllPredecessorEdges()) { + edge.getSource().removeSuccessor(edge); + } + found = true; + } + } else { + StatEdge sucedge = st.getAllSuccessorEdges().get(0); + if(sucedge.getType() != StatEdge.TYPE_FINALLYEXIT) { + st.removeSuccessor(sucedge); + + for(StatEdge edge: st.getAllPredecessorEdges()) { + if(sucedge.getType()!=StatEdge.TYPE_REGULAR) { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, sucedge.getType()); + } + + st.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, sucedge.getDestination()); + sucedge.getDestination().addPredecessor(edge); + + if(sucedge.closure != null) { + sucedge.closure.addLabeledEdge(edge); + } + } + found = true; + } + } + + if(found) { + sequence.getStats().removeWithKey(st.id); + break; + } + } + } + + if(!found) { + break; + } + } + + sequence.setFirst(sequence.getStats().get(0)); + + } + + private static void mergeFlatStatements(SequenceStatement sequence) { + + for(;;) { + + Statement next = null; + Statement current = null; + + boolean found = false; + + for(int i=sequence.getStats().size()-1;i>=0;i--) { + + next = current; + current = sequence.getStats().get(i); + + if(next != null && current.getExprents()!=null && !current.getExprents().isEmpty()) { + if(next.getExprents()!=null) { + next.getExprents().addAll(0, current.getExprents()); + current.getExprents().clear(); + found = true; + } else { + Statement first = getFirstExprentlist(next); + if(first != null) { + first.getExprents().addAll(0, current.getExprents()); + current.getExprents().clear(); + found = true; + } + } + } + } + + if(!found) { + break; + } + } + + } + + private static Statement getFirstExprentlist(Statement stat) { + + if(stat.getExprents() != null) { + return stat; + } + + switch(stat.type) { + case Statement.TYPE_IF: + case Statement.TYPE_SEQUENCE: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + return getFirstExprentlist(stat.getFirst()); + } + + return null; + } + + + public static void destroyAndFlattenStatement(Statement stat) { + + destroyStatementContent(stat, false); + + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + if(stat.getExprents() == null) { + bstat.setExprents(new ArrayList<Exprent>()); + } else { + bstat.setExprents(DecHelper.copyExprentList(stat.getExprents())); + } + + stat.getParent().replaceStatement(stat, bstat); + } + + public static void destroyStatementContent(Statement stat, boolean self) { + + for(Statement st: stat.getStats()) { + destroyStatementContent(st, true); + } + stat.getStats().clear(); + + if(self) { + for(StatEdge edge : stat.getAllSuccessorEdges()) { + stat.removeSuccessor(edge); + } + } + + } + +} |