/* * Fernflower - The Analytical Java Decompiler * http://www.reversed-java.com * * (C) 2008 - 2010, Stiver * * This software is NEITHER public domain NOR free software * as per GNU License. See license.txt for more details. * * This software is distributed WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. */ package org.jetbrains.java.decompiler.code.cfg; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.ExceptionHandler; import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.JumpInstruction; import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; import org.jetbrains.java.decompiler.code.SwitchInstruction; import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; import org.jetbrains.java.decompiler.struct.StructMethod; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.struct.gen.DataPoint; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.ListStack; import org.jetbrains.java.decompiler.util.VBStyleCollection; public class ControlFlowGraph implements CodeConstants { public int last_id = 0; // ***************************************************************************** // private fields // ***************************************************************************** private VBStyleCollection blocks; private BasicBlock first; private BasicBlock last; private List exceptions; private HashMap subroutines; private HashSet finallyExits = new HashSet(); // ***************************************************************************** // constructors // ***************************************************************************** public ControlFlowGraph(InstructionSequence seq) { buildBlocks(seq); } // ***************************************************************************** // public methods // ***************************************************************************** public void free() { for(BasicBlock block: blocks) { block.free(); } blocks.clear(); first = null; last = null; exceptions.clear(); finallyExits.clear(); } public void removeMarkers() { for(BasicBlock block: blocks) { block.mark = 0; } } public String toString() { String new_line_separator = DecompilerContext.getNewLineSeparator(); StringBuffer buf = new StringBuffer(); for(BasicBlock block: blocks) { buf.append("----- Block "+block.id+" -----" + new_line_separator); buf.append(block.toString()); buf.append("----- Edges -----" + new_line_separator); List suc = block.getSuccs(); for(int j=0;j>>>>>>>(regular) Block "+((BasicBlock)suc.get(j)).id+new_line_separator); } suc = block.getSuccExceptions(); for(int j=0;j>>>>>>>(exception) Block "+handler.id+"\t"+"ERROR: range not found!"+new_line_separator); } else { List exceptionTypes = range.getExceptionTypes(); if(exceptionTypes == null) { buf.append(">>>>>>>>(exception) Block "+handler.id+"\t"+"NULL"+new_line_separator); } else { for(String exceptionType : exceptionTypes) { buf.append(">>>>>>>>(exception) Block "+handler.id+"\t"+exceptionType+new_line_separator); } } } } buf.append("----- ----- -----" + new_line_separator); } return buf.toString(); } public void inlineJsr(StructMethod mt) { processJsr(); removeJsr(mt); removeMarkers(); DeadCodeHelper.removeEmptyBlocks(this); } public void removeBlock(BasicBlock block) { while(block.getSuccs().size()>0) { block.removeSuccessor((BasicBlock)block.getSuccs().get(0)); } while(block.getSuccExceptions().size()>0) { block.removeSuccessorException((BasicBlock)block.getSuccExceptions().get(0)); } while(block.getPreds().size()>0) { ((BasicBlock)block.getPreds().get(0)).removeSuccessor(block); } while(block.getPredExceptions().size()>0) { ((BasicBlock)block.getPredExceptions().get(0)).removeSuccessorException(block); } last.removePredecessor(block); blocks.removeWithKey(block.id); for(int i=exceptions.size()-1;i>=0;i--) { ExceptionRangeCFG range = (ExceptionRangeCFG)exceptions.get(i); if(range.getHandler() == block) { exceptions.remove(i); } else { List lstRange = range.getProtectedRange(); lstRange.remove(block); if(lstRange.isEmpty()) { exceptions.remove(i); } } } Iterator> it = subroutines.entrySet().iterator(); while(it.hasNext()) { Entry ent = it.next(); if(ent.getKey() == block || ent.getValue() == block) { it.remove(); } } } public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) { //List ranges = new ArrayList(); for(int i=exceptions.size()-1;i>=0;i--) { ExceptionRangeCFG range = exceptions.get(i); if(range.getHandler() == handler && range.getProtectedRange().contains(block)) { return range; //ranges.add(range); } } return null; //return ranges.isEmpty() ? null : ranges; } // public String getExceptionsUniqueString(BasicBlock handler, BasicBlock block) { // // List ranges = getExceptionRange(handler, block); // // if(ranges == null) { // return null; // } else { // Set setExceptionStrings = new HashSet(); // for(ExceptionRangeCFG range : ranges) { // setExceptionStrings.add(range.getExceptionType()); // } // // String ret = ""; // for(String exception : setExceptionStrings) { // ret += exception; // } // // return ret; // } // } // ***************************************************************************** // private methods // ***************************************************************************** private void buildBlocks(InstructionSequence instrseq) { short[] states = findStartInstructions(instrseq); HashMap mapInstrBlocks = new HashMap(); VBStyleCollection colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks); blocks = colBlocks; connectBlocks(colBlocks, mapInstrBlocks); setExceptionEdges(instrseq, mapInstrBlocks); setSubroutineEdges(); setFirstAndLastBlocks(); } private short[] findStartInstructions(InstructionSequence seq) { int len = seq.length(); short[] inststates = new short[len]; HashSet excSet = new HashSet(); for(ExceptionHandler handler : seq.getExceptionTable().getHandlers()) { excSet.add(handler.from_instr); excSet.add(handler.to_instr); excSet.add(handler.handler_instr); } for(int i=0;i=0;j--) { inststates[dests[j]] = 1; } inststates[swinstr.getDefaultdest()] = 1; if(i+1 < len) { inststates[i+1] = 1; } } } // first instruction inststates[0] = 1; return inststates; } private VBStyleCollection createBasicBlocks(short[] startblock, InstructionSequence instrseq, HashMap mapInstrBlocks) { VBStyleCollection col = new VBStyleCollection(); InstructionSequence currseq = null; ArrayList lstOffs = null; int len = startblock.length; short counter = 0; int blockoffset = 0; BasicBlock currentBlock = null; for(int i=0;i(); currentBlock.setSeq(currseq); currentBlock.setInstrOldOffsets(lstOffs); col.addWithKey(currentBlock, currentBlock.id); blockoffset = instrseq.getOffset(i); } startblock[i] = counter; mapInstrBlocks.put(i, currentBlock); currseq.addInstruction(instrseq.getInstr(i), instrseq.getOffset(i)-blockoffset); lstOffs.add(instrseq.getOffset(i)); } last_id = counter; return col; } private void connectBlocks(List lstbb, HashMap mapInstrBlocks) { for(int i=0;i instrBlocks) { exceptions = new ArrayList(); Map mapRanges = new HashMap(); for(ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) { BasicBlock from = instrBlocks.get(handler.from_instr); BasicBlock to = instrBlocks.get(handler.to_instr); BasicBlock handle = instrBlocks.get(handler.handler_instr); String key = from.id + ":" + to.id + ":" + handle.id; if(mapRanges.containsKey(key)) { ExceptionRangeCFG range = mapRanges.get(key); range.addExceptionType(handler.exceptionClass); } else { List protectedRange = new ArrayList(); for(int j=from.id;j subroutines = new HashMap(); for(BasicBlock block : blocks) { if(block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) { LinkedList stack = new LinkedList(); LinkedList> stackJsrStacks = new LinkedList>(); HashSet setVisited = new HashSet(); stack.add(block); stackJsrStacks.add(new LinkedList()); while(!stack.isEmpty()) { BasicBlock node = stack.removeFirst(); LinkedList jsrstack = stackJsrStacks.removeFirst(); setVisited.add(node); switch(node.getSeq().getLastInstr().opcode) { case CodeConstants.opc_jsr: jsrstack.add(node); break; case CodeConstants.opc_ret: BasicBlock enter = jsrstack.getLast(); BasicBlock exit = blocks.getWithKey(enter.id + 1); // FIXME: find successor in a better way if(exit!=null) { if(!node.isSuccessor(exit)) { node.addSuccessor(exit); } jsrstack.removeLast(); subroutines.put(enter, exit); } else { throw new RuntimeException("ERROR: last instruction jsr"); } } if(!jsrstack.isEmpty()) { for(BasicBlock succ : node.getSuccs()) { if(!setVisited.contains(succ)) { stack.add(succ); stackJsrStacks.add(new LinkedList(jsrstack)); } } } } } } this.subroutines = subroutines; } private void processJsr() { while(processJsrRanges()!=0); } private int processJsrRanges() { List lstJsrAll = new ArrayList(); // get all jsr ranges for(Entry ent : subroutines.entrySet()){ BasicBlock jsr = ent.getKey(); BasicBlock ret = ent.getValue(); lstJsrAll.add(new Object[]{jsr, getJsrRange(jsr, ret), ret}); } // sort ranges // FIXME: better sort order List lstJsr = new ArrayList(); for(Object[] arr : lstJsrAll) { int i=0; for(;i)arrJsr[1]).contains(arr[0])) { break; } } lstJsr.add(i, arr); } // find the first intersection for(int i=0;i set = (HashSet)arr[1]; for(int j=i+1;j set1 = (HashSet)arr1[1]; if(!set.contains(arr1[0]) && !set1.contains(arr[0])) { // rang 0 doesn't contain entry 1 and vice versa HashSet setc = new HashSet(set); setc.retainAll(set1); if(!setc.isEmpty()) { splitJsrRange((BasicBlock)arr[0], (BasicBlock)arr[2], setc); return 1; } } } } return 0; } private HashSet getJsrRange(BasicBlock jsr, BasicBlock ret) { HashSet blocks = new HashSet(); LinkedList lstNodes = new LinkedList(); lstNodes.add(jsr); BasicBlock dom = jsr.getSuccs().get(0); while(!lstNodes.isEmpty()) { BasicBlock node = lstNodes.remove(0); for(int j=0;j<2;j++) { List lst; if(j==0) { if(node.getLastInstruction().opcode == CodeConstants.opc_ret) { if(node.getSuccs().contains(ret)) { continue; } } lst = node.getSuccs(); } else { if(node == jsr) { continue; } lst = node.getSuccExceptions(); } CHILD: for(int i=lst.size()-1;i>=0;i--) { BasicBlock child = lst.get(i); if(!blocks.contains(child)) { if(node != jsr) { for(int k=0;k common_blocks) { LinkedList lstNodes = new LinkedList(); HashMap mapNewNodes = new HashMap(); lstNodes.add(jsr); mapNewNodes.put(jsr.id, jsr); while(!lstNodes.isEmpty()) { BasicBlock node = lstNodes.remove(0); for(int j=0;j<2;j++) { List lst; if(j==0) { if(node.getLastInstruction().opcode == CodeConstants.opc_ret) { if(node.getSuccs().contains(ret)) { continue; } } lst = node.getSuccs(); } else { if(node == jsr) { continue; } lst = node.getSuccExceptions(); } for(int i=lst.size()-1;i>=0;i--) { BasicBlock child = (BasicBlock)lst.get(i); Integer childid = child.id; if(mapNewNodes.containsKey(childid)) { node.replaceSuccessor(child, (BasicBlock)mapNewNodes.get(childid)); } else if(common_blocks.contains(child)) { // make a copy of the current block BasicBlock copy = (BasicBlock)child.clone(); copy.id = ++last_id; // copy all successors if(copy.getLastInstruction().opcode == CodeConstants.opc_ret && child.getSuccs().contains(ret)) { copy.addSuccessor(ret); child.removeSuccessor(ret); } else { for(int k=0;k common_blocks, HashMap mapNewNodes) { for(int i=exceptions.size()-1;i>=0;i--) { ExceptionRangeCFG range = (ExceptionRangeCFG)exceptions.get(i); List lstRange = range.getProtectedRange(); HashSet setBoth = new HashSet(common_blocks); setBoth.retainAll(lstRange); if(setBoth.size()>0) { List lstNewRange; if(setBoth.size()==lstRange.size()) { lstNewRange = new ArrayList(); ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange, (BasicBlock)mapNewNodes.get(range.getHandler().id),range.getExceptionTypes()); exceptions.add(newRange); } else { lstNewRange = lstRange; } for(BasicBlock block : setBoth) { lstNewRange.add(mapNewNodes.get(block.id)); } } } } private void removeJsr(StructMethod mt) { removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt)); } private void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) { ListStack stack = data.getStack(); InstructionSequence seq = block.getSeq(); for(int i=0;i(data.getLocalVariables())); point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null)); removeJsrInstructions(pool, suc, point); } } } private void setFirstAndLastBlocks() { first = blocks.get(0); last = new BasicBlock(); last.id = ++last_id; last.setSeq(new SimpleInstructionSequence()); for(BasicBlock block: blocks) { if(block.getSuccs().isEmpty()) { last.addPredecessor(block); } } } public List getReversePostOrder() { LinkedList res = new LinkedList(); addToReversePostOrderListIterative(first, res); return res; } private void addToReversePostOrderListIterative(BasicBlock root, List lst) { LinkedList stackNode = new LinkedList(); LinkedList stackIndex = new LinkedList(); HashSet setVisited = new HashSet(); stackNode.add(root); stackIndex.add(0); while(!stackNode.isEmpty()) { BasicBlock node = stackNode.getLast(); int index = stackIndex.removeLast(); setVisited.add(node); List lstSuccs = new ArrayList(node.getSuccs()); lstSuccs.addAll(node.getSuccExceptions()); for(;index getBlocks() { return blocks; } public void setBlocks(VBStyleCollection blocks) { this.blocks = blocks; } public BasicBlock getFirst() { return first; } public void setFirst(BasicBlock first) { this.first = first; } public List getEndBlocks() { return last.getPreds(); } public List getExceptions() { return exceptions; } public void setExceptions(List exceptions) { this.exceptions = exceptions; } public BasicBlock getLast() { return last; } public void setLast(BasicBlock last) { this.last = last; } public HashMap getSubroutines() { return subroutines; } public void setSubroutines(HashMap subroutines) { this.subroutines = subroutines; } public HashSet getFinallyExits() { return finallyExits; } public void setFinallyExits(HashSet finallyExits) { this.finallyExits = finallyExits; } }