diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java new file mode 100644 index 000000000000..063d201f81f0 --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java @@ -0,0 +1,327 @@ +/* + * 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.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + + +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: + while (true) { + 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); + + while (true) { + + 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) { + + while (true) { + + 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); + } + } + } +} |