diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java new file mode 100644 index 000000000000..b54642dfb58f --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -0,0 +1,367 @@ +/* + * 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.stats; + +import org.jetbrains.java.decompiler.code.SwitchInstruction; +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.DecHelper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +import java.util.*; + +public class SwitchStatement extends Statement { + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private List<Statement> caseStatements = new ArrayList<Statement>(); + + private List<List<StatEdge>> caseEdges = new ArrayList<List<StatEdge>>(); + + private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>(); + + private StatEdge default_edge; + + private List<Exprent> headexprent = new ArrayList<Exprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private SwitchStatement() { + type = TYPE_SWITCH; + + headexprent.add(null); + } + + private SwitchStatement(Statement head, Statement poststat) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + // find post node + Set<Statement> lstNodes = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_FORWARD)); + + // cluster nodes + if (poststat != null) { + post = poststat; + lstNodes.remove(post); + } + + default_edge = head.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); + + for (Statement st : lstNodes) { + stats.addWithKey(st, st.id); + } + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if (head.type == Statement.TYPE_BASICBLOCK && head.getLastBasicType() == Statement.LASTBASICTYPE_SWITCH) { + + List<Statement> lst = new ArrayList<Statement>(); + if (DecHelper.isChoiceStatement(head, lst)) { + Statement post = lst.remove(0); + + for (Statement st : lst) { + if (st.isMonitorEnter()) { + return null; + } + } + + if (DecHelper.checkStatementExceptions(lst)) { + return new SwitchStatement(head, post); + } + } + } + + return null; + } + + public String toJava(int indent) { + + String indstr = InterpreterUtil.getIndentString(indent); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuilder buf = new StringBuilder(); + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + buf.append(first.toJava(indent)); + + if (isLabeled()) { + buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); + } + + buf.append(indstr).append(headexprent.get(0).toJava(indent)).append(" {").append(new_line_separator); + + VarType switch_type = headexprent.get(0).getExprType(); + + for (int i = 0; i < caseStatements.size(); i++) { + + Statement stat = caseStatements.get(i); + List<StatEdge> edges = caseEdges.get(i); + List<ConstExprent> values = caseValues.get(i); + + for (int j = 0; j < edges.size(); j++) { + if (edges.get(j) == default_edge) { + buf.append(indstr).append("default:").append(new_line_separator); + } + else { + ConstExprent value = (ConstExprent)values.get(j).copy(); + value.setConsttype(switch_type); + + buf.append(indstr).append("case ").append(value.toJava(indent)).append(":").append(new_line_separator); + } + } + + buf.append(ExprProcessor.jmpWrapper(stat, indent + 1, false)); + } + + buf.append(indstr).append("}").append(new_line_separator); + + return buf.toString(); + } + + public void initExprents() { + SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size() - 1); + swexpr.setCaseValues(caseValues); + + headexprent.set(0, swexpr); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(stats); + lst.add(1, headexprent.get(0)); + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if (headexprent.get(0) == oldexpr) { + headexprent.set(0, newexpr); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + for (int i = 0; i < caseStatements.size(); i++) { + if (caseStatements.get(i) == oldstat) { + caseStatements.set(i, newstat); + } + } + + super.replaceStatement(oldstat, newstat); + } + + public Statement getSimpleCopy() { + return new SwitchStatement(); + } + + public void initSimpleCopy() { + first = stats.get(0); + default_edge = first.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); + + sortEdgesAndNodes(); + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + public void sortEdgesAndNodes() { + + HashMap<StatEdge, Integer> mapEdgeIndex = new HashMap<StatEdge, Integer>(); + + List<StatEdge> lstFirstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + for (int i = 0; i < lstFirstSuccs.size(); i++) { + mapEdgeIndex.put(lstFirstSuccs.get(i), i == 0 ? lstFirstSuccs.size() : i); + } + + // case values + BasicBlockStatement bbstat = (BasicBlockStatement)first; + int[] values = ((SwitchInstruction)bbstat.getBlock().getLastInstruction()).getValues(); + + List<Statement> nodes = new ArrayList<Statement>(); + List<List<Integer>> edges = new ArrayList<List<Integer>>(); + + // collect regular edges + for (int i = 1; i < stats.size(); i++) { + + Statement stat = stats.get(i); + + List<Integer> lst = new ArrayList<Integer>(); + for (StatEdge edge : stat.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { + if (edge.getSource() == first) { + lst.add(mapEdgeIndex.get(edge)); + } + } + Collections.sort(lst); + + nodes.add(stat); + edges.add(lst); + } + + // collect exit edges + List<StatEdge> lstExitEdges = first.getSuccessorEdges(StatEdge.TYPE_BREAK | StatEdge.TYPE_CONTINUE); + while (!lstExitEdges.isEmpty()) { + StatEdge edge = lstExitEdges.get(0); + + List<Integer> lst = new ArrayList<Integer>(); + for (int i = lstExitEdges.size() - 1; i >= 0; i--) { + StatEdge edgeTemp = lstExitEdges.get(i); + if (edgeTemp.getDestination() == edge.getDestination() && edgeTemp.getType() == edge.getType()) { + lst.add(mapEdgeIndex.get(edgeTemp)); + lstExitEdges.remove(i); + } + } + Collections.sort(lst); + + nodes.add(null); + edges.add(lst); + } + + // sort edges (bubblesort) + for (int i = 0; i < edges.size() - 1; i++) { + for (int j = edges.size() - 1; j > i; j--) { + if (edges.get(j - 1).get(0) > edges.get(j).get(0)) { + edges.set(j, edges.set(j - 1, edges.get(j))); + nodes.set(j, nodes.set(j - 1, nodes.get(j))); + } + } + } + + // sort statement cliques + for (int index = 0; index < nodes.size(); index++) { + Statement stat = nodes.get(index); + + if (stat != null) { + HashSet<Statement> setPreds = new HashSet<Statement>(stat.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_BACKWARD)); + setPreds.remove(first); + + if (!setPreds.isEmpty()) { + Statement pred = + setPreds.iterator().next(); // assumption: at most one predecessor node besides the head. May not hold true for obfuscated code. + for (int j = 0; j < nodes.size(); j++) { + if (j != (index - 1) && nodes.get(j) == pred) { + nodes.add(j + 1, stat); + edges.add(j + 1, edges.get(index)); + + if (j > index) { + nodes.remove(index); + edges.remove(index); + index--; + } + else { + nodes.remove(index + 1); + edges.remove(index + 1); + } + break; + } + } + } + } + } + + // translate indices back into edges + List<List<StatEdge>> lstEdges = new ArrayList<List<StatEdge>>(); + List<List<ConstExprent>> lstValues = new ArrayList<List<ConstExprent>>(); + + for (List<Integer> lst : edges) { + List<StatEdge> lste = new ArrayList<StatEdge>(); + List<ConstExprent> lstv = new ArrayList<ConstExprent>(); + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + for (Integer in : lst) { + int index = in == lstSuccs.size() ? 0 : in; + + lste.add(lstSuccs.get(index)); + lstv.add(index == 0 ? null : new ConstExprent(values[index - 1], false)); + } + lstEdges.add(lste); + lstValues.add(lstv); + } + + // replace null statements with dummy basic blocks + for (int i = 0; i < nodes.size(); i++) { + if (nodes.get(i) == null) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + + StatEdge sample_edge = lstEdges.get(i).get(0); + + bstat.addSuccessor(new StatEdge(sample_edge.getType(), bstat, sample_edge.getDestination(), sample_edge.closure)); + + for (StatEdge edge : lstEdges.get(i)) { + + edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR); + edge.closure.getLabelEdges().remove(edge); + + edge.getDestination().removePredecessor(edge); + edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, bstat); + bstat.addPredecessor(edge); + } + + nodes.set(i, bstat); + stats.addWithKey(bstat, bstat.id); + bstat.setParent(this); + } + } + + caseStatements = nodes; + caseEdges = lstEdges; + caseValues = lstValues; + } + + public List<Exprent> getHeadexprentList() { + return headexprent; + } + + public Exprent getHeadexprent() { + return headexprent.get(0); + } + + public List<List<StatEdge>> getCaseEdges() { + return caseEdges; + } + + public List<Statement> getCaseStatements() { + return caseStatements; + } + + public StatEdge getDefault_edge() { + return default_edge; + } + + public List<List<ConstExprent>> getCaseValues() { + return caseValues; + } +} |