/* * 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.BytecodeMappingTracer; 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.*; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.Util; import java.util.*; public class SwitchStatement extends Statement { // ***************************************************************************** // private fields // ***************************************************************************** private List caseStatements = new ArrayList(); private List> caseEdges = new ArrayList>(); private List> caseValues = new ArrayList>(); private StatEdge default_edge; private List headexprent = new ArrayList(); // ***************************************************************************** // 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 lstNodes = new HashSet(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 lst = new ArrayList(); 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, BytecodeMappingTracer tracer) { String indstr = InterpreterUtil.getIndentString(indent); String new_line_separator = DecompilerContext.getNewLineSeparator(); StringBuilder buf = new StringBuilder(); String content = ExprProcessor.listToJava(varDefinitions, indent, tracer); buf.append(content); if (!content.isEmpty()) { buf.append(new_line_separator); } content = first.toJava(indent, tracer); buf.append(content); if (first instanceof BasicBlockStatement && !content.isEmpty()) { List exps = first.getExprents(); if (exps.size() != 0) { Exprent e = exps.get(exps.size() - 1); if (!(e instanceof InvocationExprent || e instanceof FunctionExprent || (e instanceof AssignmentExprent && !(((AssignmentExprent) e).getLeft() instanceof VarExprent && ((VarExprent) ((AssignmentExprent) e).getLeft()).isDefinition())))) { buf.append(new_line_separator); } } } if (isLabeled()) { buf.append(indstr).append("label").append(this.id).append(":").append(new_line_separator); tracer.incrementCurrentSourceLine(); } buf.append(indstr).append(headexprent.get(0).toJava(indent, tracer)).append(" {").append(new_line_separator); tracer.incrementCurrentSourceLine(); VarType switch_type = headexprent.get(0).getExprType(); for (int i = 0; i < caseStatements.size(); i++) { Statement stat = caseStatements.get(i); List edges = caseEdges.get(i); List 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); tracer.incrementCurrentSourceLine(); } else { ConstExprent value = (ConstExprent)values.get(j).copy(); value.setConsttype(switch_type); buf.append(indstr).append("case ").append(value.toJava(indent, tracer)).append(":").append(new_line_separator); tracer.incrementCurrentSourceLine(); } } String c = Util.rtrim(ExprProcessor.jmpWrapper(stat, indent + 1, false, tracer)); if (!c.isEmpty()) { buf.append(c); buf.append(new_line_separator); } if (i != caseStatements.size() - 1) buf.append(new_line_separator); } buf.append(indstr).append("}").append(new_line_separator); tracer.incrementCurrentSourceLine(); 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 getSequentialObjects() { List lst = new ArrayList(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 mapEdgeIndex = new HashMap(); List 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 nodes = new ArrayList(); List> edges = new ArrayList>(); // collect regular edges for (int i = 1; i < stats.size(); i++) { Statement stat = stats.get(i); List lst = new ArrayList(); 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 lstExitEdges = first.getSuccessorEdges(StatEdge.TYPE_BREAK | StatEdge.TYPE_CONTINUE); while (!lstExitEdges.isEmpty()) { StatEdge edge = lstExitEdges.get(0); List lst = new ArrayList(); 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 setPreds = new HashSet(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> lstEdges = new ArrayList>(); List> lstValues = new ArrayList>(); for (List lst : edges) { List lste = new ArrayList(); List lstv = new ArrayList(); List 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 getHeadexprentList() { return headexprent; } public Exprent getHeadexprent() { return headexprent.get(0); } public List> getCaseEdges() { return caseEdges; } public List getCaseStatements() { return caseStatements; } public StatEdge getDefault_edge() { return default_edge; } public List> getCaseValues() { return caseValues; } }