| /* |
| * 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); |
| } |
| } |
| } |
| } |