summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java
diff options
context:
space:
mode:
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.java327
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);
+ }
+ }
+ }
+}