/* * 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.modules.code; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.code.Instruction; import org.jetbrains.java.decompiler.code.InstructionSequence; import org.jetbrains.java.decompiler.code.cfg.BasicBlock; import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph; import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; public class DeadCodeHelper { public static void removeDeadBlocks(ControlFlowGraph graph) { LinkedList stack = new LinkedList(); HashSet setStacked = new HashSet(); stack.add(graph.getFirst()); setStacked.add(graph.getFirst()); while(!stack.isEmpty()) { BasicBlock block = stack.removeFirst(); List lstSuccs = new ArrayList(block.getSuccs()); lstSuccs.addAll(block.getSuccExceptions()); for(BasicBlock succ : lstSuccs) { if(!setStacked.contains(succ)) { stack.add(succ); setStacked.add(succ); } } } HashSet setAllBlocks = new HashSet(graph.getBlocks()); setAllBlocks.removeAll(setStacked); for(BasicBlock block : setAllBlocks) { graph.removeBlock(block); } } public static void removeEmptyBlocks(ControlFlowGraph graph) { List blocks = graph.getBlocks(); boolean cont; do { cont = false; for(int i=blocks.size()-1;i>=0;i--) { BasicBlock block = (BasicBlock)blocks.get(i); if(removeEmptyBlock(graph, block, false)) { cont = true; break; } } } while(cont); } private static boolean removeEmptyBlock(ControlFlowGraph graph, BasicBlock block, boolean merging) { boolean deletedRanges = false; if(block.getSeq().isEmpty()) { if(block.getSuccs().size() > 1) { if(block.getPreds().size()>1) { // ambiguous block throw new RuntimeException("ERROR: empty block with multiple predecessors and successors found"); } else if(!merging) { throw new RuntimeException("ERROR: empty block with multiple successors found"); } } HashSet setExits = new HashSet(graph.getLast().getPreds()); if(block.getPredExceptions().isEmpty() && (!setExits.contains(block) || block.getPreds().size() == 1)) { if(setExits.contains(block)) { BasicBlock pred = block.getPreds().get(0); // FIXME: flag in the basic block if(pred.getSuccs().size() != 1 || (!pred.getSeq().isEmpty() && pred.getSeq().getLastInstr().group == CodeConstants.GROUP_SWITCH)) { return false; } } HashSet setPreds = new HashSet(block.getPreds()); HashSet setSuccs = new HashSet(block.getSuccs()); // collect common exception ranges of predecessors and successors HashSet setCommonExceptionHandlers = null; for(int i = 0; i < 2; ++i) { for(BasicBlock pred : i == 0 ? setPreds : setSuccs) { if(setCommonExceptionHandlers == null) { setCommonExceptionHandlers = new HashSet(pred.getSuccExceptions()); } else { setCommonExceptionHandlers.retainAll(pred.getSuccExceptions()); } } } // check the block to be in each of the common ranges if(setCommonExceptionHandlers != null && !setCommonExceptionHandlers.isEmpty()) { for(BasicBlock handler : setCommonExceptionHandlers) { if(!block.getSuccExceptions().contains(handler)) { return false; } } } // remove ranges consisting of this one block List lstRanges = graph.getExceptions(); for(int i=lstRanges.size()-1;i>=0;i--) { ExceptionRangeCFG range = lstRanges.get(i); List lst = range.getProtectedRange(); if(lst.size() == 1 && lst.get(0) == block) { if(DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) { block.removeSuccessorException(range.getHandler()); lstRanges.remove(i); deletedRanges = true; } else { return false; } } } // connect remaining nodes if(merging) { BasicBlock pred = block.getPreds().get(0); pred.removeSuccessor(block); List lstSuccs = new ArrayList(block.getSuccs()); for(BasicBlock succ : lstSuccs) { block.removeSuccessor(succ); pred.addSuccessor(succ); } } else { for(BasicBlock pred : setPreds) { for(BasicBlock succ : setSuccs) { pred.replaceSuccessor(block, succ); } } } // finally exit edges HashSet setFinallyExits = graph.getFinallyExits(); if(setFinallyExits.contains(block)) { setFinallyExits.remove(block); setFinallyExits.add(setPreds.iterator().next()); } // replace first if necessary if(graph.getFirst() == block) { if(setSuccs.size() != 1) { throw new RuntimeException("multiple or no entry blocks!"); } else { graph.setFirst(setSuccs.iterator().next()); } } // remove this block graph.removeBlock(block); if(deletedRanges) { DeadCodeHelper.removeDeadBlocks(graph); } } } return deletedRanges; } public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) { HashSet marked = new HashSet(); if(block == dom) { return true; } LinkedList lstNodes = new LinkedList(); lstNodes.add(block); while(!lstNodes.isEmpty()) { BasicBlock node = (BasicBlock)lstNodes.remove(0); if(marked.contains(node)) { continue; } else { marked.add(node); } if(node == graph.getFirst()) { return false; } for(int i=0;i(exit.getPreds())) { exit.removePredecessor(block); block.addSuccessor(exit); } } public static void incorporateValueReturns(ControlFlowGraph graph) { for(BasicBlock block: graph.getBlocks()) { InstructionSequence seq = block.getSeq(); int len = seq.length(); if(len > 0 && len < 3) { boolean ok = false; if(seq.getLastInstr().opcode >= CodeConstants.opc_ireturn && seq.getLastInstr().opcode <= CodeConstants.opc_return) { if(len == 1) { ok = true; } else if(seq.getLastInstr().opcode != CodeConstants.opc_return){ switch(seq.getInstr(0).opcode) { case CodeConstants.opc_iload: case CodeConstants.opc_lload: case CodeConstants.opc_fload: case CodeConstants.opc_dload: case CodeConstants.opc_aload: case CodeConstants.opc_aconst_null: case CodeConstants.opc_bipush: case CodeConstants.opc_sipush: case CodeConstants.opc_lconst_0: case CodeConstants.opc_lconst_1: case CodeConstants.opc_fconst_0: case CodeConstants.opc_fconst_1: case CodeConstants.opc_fconst_2: case CodeConstants.opc_dconst_0: case CodeConstants.opc_dconst_1: case CodeConstants.opc_ldc: case CodeConstants.opc_ldc_w: case CodeConstants.opc_ldc2_w: ok = true; } } } if(ok) { if(!block.getPreds().isEmpty()) { HashSet setPredHandlersUnion = new HashSet(); HashSet setPredHandlersIntersection = new HashSet(); boolean firstpred = true; for(BasicBlock pred : block.getPreds()) { if(firstpred) { setPredHandlersIntersection.addAll(pred.getSuccExceptions()); firstpred = false; } else { setPredHandlersIntersection.retainAll(pred.getSuccExceptions()); } setPredHandlersUnion.addAll(pred.getSuccExceptions()); } // add exception ranges from predecessors setPredHandlersIntersection.removeAll(block.getSuccExceptions()); BasicBlock predecessor = block.getPreds().get(0); for(BasicBlock handler : setPredHandlersIntersection) { ExceptionRangeCFG range = graph.getExceptionRange(handler, predecessor); range.getProtectedRange().add(block); block.addSuccessorException(handler); } // remove redundant ranges HashSet setRangesToBeRemoved = new HashSet(block.getSuccExceptions()); setRangesToBeRemoved.removeAll(setPredHandlersUnion); for(BasicBlock handler : setRangesToBeRemoved) { ExceptionRangeCFG range = graph.getExceptionRange(handler, block); if(range.getProtectedRange().size() > 1) { range.getProtectedRange().remove(block); block.removeSuccessorException(handler); } } } if(block.getPreds().size() == 1 && block.getPredExceptions().isEmpty()) { BasicBlock bpred = block.getPreds().get(0); if(bpred.getSuccs().size() == 1) { // add exception ranges of predecessor for(BasicBlock succ : bpred.getSuccExceptions()) { if(!block.getSuccExceptions().contains(succ)) { ExceptionRangeCFG range = graph.getExceptionRange(succ, bpred); range.getProtectedRange().add(block); block.addSuccessorException(succ); } } // remove superfluous ranges from successors for(BasicBlock succ : new HashSet(block.getSuccExceptions())) { if(!bpred.getSuccExceptions().contains(succ)) { ExceptionRangeCFG range = graph.getExceptionRange(succ, block); if(range.getProtectedRange().size() > 1) { range.getProtectedRange().remove(block); block.removeSuccessorException(succ); } } } } } } } } } public static void mergeBasicBlocks(ControlFlowGraph graph) { for(;;) { boolean merged = false; for(BasicBlock block: graph.getBlocks()) { InstructionSequence seq = block.getSeq(); if(block.getSuccs().size() == 1) { BasicBlock next = block.getSuccs().get(0); if(next != graph.getLast() && (seq.isEmpty() || seq.getLastInstr().group != CodeConstants.GROUP_SWITCH)) { if(next.getPreds().size() == 1 && next.getPredExceptions().isEmpty() && next != graph.getFirst()) { // TODO: implement a dummy start block boolean sameRanges = true; for(ExceptionRangeCFG range : graph.getExceptions()) { if(range.getProtectedRange().contains(block) ^ range.getProtectedRange().contains(next)) { sameRanges = false; break; } } if(sameRanges) { seq.addSequence(next.getSeq()); next.getSeq().clear(); removeEmptyBlock(graph, next, true); merged = true; break; } } } } } if(!merged) { break; } } } }