/* TransformExceptionHandlers Copyright (C) 1998-2002 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; see the file COPYING.LESSER. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package net.sf.jode.flow; import net.sf.jode.GlobalOptions; import net.sf.jode.type.Type; import net.sf.jode.decompiler.LocalInfo; import net.sf.jode.expr.*; ///#def COLLECTIONS java.util import java.util.TreeSet; import java.util.SortedSet; import java.util.Set; import java.util.Iterator; ///#enddef ///#def COLLECTIONEXTRA java.lang import java.lang.Comparable; ///#enddef /** * * @author Jochen Hoenicke */ public class TransformExceptionHandlers { SortedSet handlers; FlowBlock[] flowBlocks; static class Handler implements Comparable { FlowBlock start; FlowBlock end; FlowBlock handler; Type type; public Handler(FlowBlock tryBlock, FlowBlock endBlock, FlowBlock catchBlock, Type type) { this.start = tryBlock; this.end = endBlock; this.handler = catchBlock; this.type = type; } public int compareTo (Object o) { Handler second = (Handler) o; /* First sort by start offsets, highest block number first...*/ if (start.getBlockNr() != second.start.getBlockNr()) /* this subtraction is save since block numbers are only 16 bit */ return second.start.getBlockNr() - start.getBlockNr(); /* ...Second sort by end offsets, lowest block number first... * this will move the innermost blocks to the beginning. */ if (end.getBlockNr() != second.end.getBlockNr()) return end.getBlockNr() - second.end.getBlockNr(); /* ...Last sort by handler offsets, lowest first */ if (handler.getBlockNr() != second.handler.getBlockNr()) return handler.getBlockNr() - second.handler.getBlockNr(); /* ...Last sort by typecode signature. Shouldn't happen to often. */ if (type == second.type) return 0; if (type == null) return -1; if (second.type == null) return 1; return type.getTypeSignature() .compareTo(second.type.getTypeSignature()); } } public TransformExceptionHandlers(FlowBlock[] flowBlocks) { handlers = new TreeSet(); this.flowBlocks = flowBlocks; } /** * Add an exception Handler. * @param start The start block number of the exception range. * @param end The end block number of the exception range + 1. * @param handler The block number of the handler. * @param type The type of the exception, null for ALL. */ public void addHandler(FlowBlock tryBlock, FlowBlock endBlock, FlowBlock catchBlock, Type type) { handlers.add(new Handler(tryBlock, endBlock, catchBlock, type)); } /** * Merge the try flow block with the catch flow block. This is a kind * of special T2 transformation, as all jumps to the catch block are * implicit (exception can be thrown everywhere).
* * This method doesn't actually merge the contents of the blocks. The * caller should do it right afterwards.
* * The flow block catchFlow mustn't have any predecessors. * @param tryFlow the flow block containing the try. * @param catchFlow the flow block containing the catch handler. */ static void mergeTryCatch(FlowBlock tryFlow, FlowBlock catchFlow) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) GlobalOptions.err.println ("mergeTryCatch(" + tryFlow.getBlockNr() + ", " + catchFlow.getBlockNr() + ")"); tryFlow.updateInOutCatch(catchFlow); tryFlow.mergeSuccessors(catchFlow); tryFlow.mergeBlockNr(catchFlow); } /** * Analyzes a simple try/catch block. The try and catch part are both * analyzed, the try block is already created, but the catch block * isn't.
* The catchFlow block mustn't have any predecessors. * * @param type The type of the exception which is caught. * @param tryFlow The flow block containing the try. The contained * block must be a try block. * @param catchFlow the flow block containing the catch handler. */ static void analyzeCatchBlock(Type type, FlowBlock tryFlow, FlowBlock catchFlow) { /* Merge try and catch flow blocks */ mergeTryCatch(tryFlow, catchFlow); /* Insert catch block into tryFlow */ CatchBlock newBlock = new CatchBlock(type); ((TryBlock)tryFlow.block).addCatchBlock(newBlock); newBlock.setCatchBlock(catchFlow.block); tryFlow.lastModified = tryFlow.block; } /** * This transforms a sub routine, i.e. it checks if the beginning * local assignment matches the final ret and removes both. It also * accepts sub routines that just pop their return address. */ boolean transformSubRoutine(StructuredBlock subRoutineBlock) { StructuredBlock firstBlock = subRoutineBlock; if (firstBlock instanceof SequentialBlock) firstBlock = subRoutineBlock.getSubBlocks()[0]; LocalInfo local = null; if (firstBlock instanceof SpecialBlock) { SpecialBlock popBlock = (SpecialBlock) firstBlock; if (popBlock.type != SpecialBlock.POP || popBlock.count != 1) return false; } else if (firstBlock instanceof InstructionBlock) { Expression expr = ((InstructionBlock) firstBlock).getInstruction(); if (expr instanceof StoreInstruction && ((StoreInstruction) expr).getLValue() instanceof LocalStoreOperator) { LocalStoreOperator store = (LocalStoreOperator) ((StoreInstruction)expr).getLValue(); local = store.getLocalInfo(); expr = ((StoreInstruction) expr).getSubExpressions()[1]; } if (!(expr instanceof NopOperator)) return false; } else return false; /* We are now committed and can start changing code. Remove * the first Statement which stores/removes the return * address. */ firstBlock.removeBlock(); /* We don't check if there is a RET in the middle. * * This is a complicated task which isn't needed for javac nor * jikes. We just check if the last instruction is a ret and * remove this. This will never produce code with wrong semantic, * as long as the bytecode was verified correctly. */ while (subRoutineBlock instanceof SequentialBlock) subRoutineBlock = subRoutineBlock.getSubBlocks()[1]; if (subRoutineBlock instanceof RetBlock && (((RetBlock) subRoutineBlock).local.equals(local))) { subRoutineBlock.removeBlock(); } return true; } /** * Remove the locale that javac introduces to temporary store the return * value, when it executes a finally block resp. monitorexit * @param ret the ReturnBlock. */ private void removeReturnLocal(ReturnBlock ret) { StructuredBlock pred = getPredecessor(ret); if (!(pred instanceof InstructionBlock)) return; Expression instr = ((InstructionBlock) pred).getInstruction(); if (!(instr instanceof StoreInstruction)) return; Expression retInstr = ret.getInstruction(); if (!(retInstr instanceof LocalLoadOperator && ((StoreInstruction) instr).lvalueMatches ((LocalLoadOperator) retInstr))) return; Expression rvalue = ((StoreInstruction) instr).getSubExpressions()[1]; ret.setInstruction(rvalue); ret.replace(ret.outer); } /** * Remove the JSRs jumping to the specified subRoutine. The right * JSRs are marked and we can just remove them. For the other JSR * instructions we replace them with a warning. * @param tryFlow the FlowBlock of the try block. * @param subRoutine the FlowBlock of the sub routine. */ private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) { for (Jump jumps = tryFlow.removeJumps(subRoutine); jumps != null; jumps = jumps.next) { StructuredBlock prev = jumps.prev; prev.removeJump(); if (prev instanceof EmptyBlock && prev.outer instanceof JsrBlock && ((JsrBlock) prev.outer).isGood()) { StructuredBlock next = prev.outer.getNextBlock(); prev.outer.removeBlock(); if (next instanceof ReturnBlock) removeReturnLocal((ReturnBlock) next); } else { /* We have a jump to the subroutine, that is badly placed. * We complain here. */ DescriptionBlock msg = new DescriptionBlock ("ERROR: invalid jump to finally block!"); prev.appendBlock(msg); } } } private static StructuredBlock getPredecessor(StructuredBlock stmt) { if (stmt.outer instanceof SequentialBlock) { SequentialBlock seq = (SequentialBlock) stmt.outer; if (seq.subBlocks[1] == stmt) return seq.subBlocks[0]; else if (seq.outer instanceof SequentialBlock) return seq.outer.getSubBlocks()[0]; } return null; } /** * Gets the slot of the monitorexit instruction instr in the * stmt, or -1 if stmt isn't a InstructionBlock with a * monitorexit instruction. * @param stmt the stmt, may be null. */ private static int getMonitorExitSlot(StructuredBlock stmt) { if (stmt instanceof InstructionBlock) { Expression instr = ((InstructionBlock) stmt).getInstruction(); if (instr instanceof MonitorExitOperator) { MonitorExitOperator monExit = (MonitorExitOperator)instr; if (monExit.getFreeOperandCount() == 0 && (monExit.getSubExpressions()[0] instanceof LocalLoadOperator)) return ((LocalLoadOperator) monExit.getSubExpressions()[0]) .getLocalInfo().getSlot(); } } return -1; } private boolean isMonitorExitSubRoutine(FlowBlock subRoutine, LocalInfo local) { if (transformSubRoutine(subRoutine.block) && getMonitorExitSlot(subRoutine.block) == local.getSlot()) return true; return false; } private static StructuredBlock skipFinExitChain(StructuredBlock block) { StructuredBlock pred, result; if (block instanceof ReturnBlock) pred = getPredecessor(block); else pred = block; result = null; while (pred instanceof JsrBlock || getMonitorExitSlot(pred) >= 0) { result = pred; pred = getPredecessor(pred); } return result; } private void checkAndRemoveJSR(FlowBlock tryFlow, FlowBlock subRoutine, int startOutExit, int endOutExit) { Iterator iter = tryFlow.getSuccessors().iterator(); dest_loop: while (iter.hasNext()) { FlowBlock dest = (FlowBlock) iter.next(); if (dest == subRoutine) continue dest_loop; boolean isFirstJump = true; for (Jump jumps = tryFlow.getJumps(dest); jumps != null; jumps = jumps.next, isFirstJump = false) { StructuredBlock prev = jumps.prev; if (prev instanceof EmptyBlock && prev.outer instanceof JsrBlock) { /* This jump is a jsr, since it doesn't leave the * block forever, we can ignore it. */ continue; } StructuredBlock pred = skipFinExitChain(prev); if (pred instanceof JsrBlock) { JsrBlock jsr = (JsrBlock) pred; StructuredBlock jsrInner = jsr.innerBlock; if (jsrInner instanceof EmptyBlock && jsrInner.jump != null && jsrInner.jump.destination == subRoutine) { /* The jump is preceeded by the right jsr. Mark the * jsr as good. */ jsr.setGood(true); continue; } } if (pred == null && isFirstJump) { /* Now we have a jump that is not preceded by any * jsr. There's a last chance: the jump jumps * directly to a correct jsr instruction, which * lies outside the try/catch block. */ if (jumps.destination.predecessors.size() == 1 && jumps.destination.getBlockNr() >= startOutExit && jumps.destination.getNextBlockNr() <= endOutExit) { jumps.destination.analyze(startOutExit, endOutExit); StructuredBlock sb = jumps.destination.block; if (sb instanceof SequentialBlock) sb = sb.getSubBlocks()[0]; if (sb instanceof JsrBlock && sb.getSubBlocks()[0] instanceof EmptyBlock && (sb.getSubBlocks()[0].jump.destination == subRoutine)) { StructuredBlock jsrInner = sb.getSubBlocks()[0]; jumps.destination.removeSuccessor(jsrInner.jump); jsrInner.removeJump(); sb.removeBlock(); continue dest_loop; } } } /* Now we have a jump with a wrong destination. * Complain! */ DescriptionBlock msg = new DescriptionBlock("ERROR: no jsr to finally"); if (pred != null) pred.prependBlock(msg); else { prev.appendBlock(msg); msg.moveJump(prev.jump); } } } if (tryFlow.getSuccessors().contains(subRoutine)) removeJSR(tryFlow, subRoutine); } private void checkAndRemoveMonitorExit(FlowBlock tryFlow, LocalInfo local, int start, int end) { FlowBlock subRoutine = null; Iterator succs = tryFlow.getSuccessors().iterator(); dest_loop: while (succs.hasNext()) { boolean isFirstJump = true; FlowBlock successor = (FlowBlock) succs.next(); for (Jump jumps = tryFlow.getJumps(successor); jumps != null; jumps = jumps.next, isFirstJump = false) { StructuredBlock prev = jumps.prev; if (prev instanceof EmptyBlock && prev.outer instanceof JsrBlock) { /* This jump is really a jsr, since it doesn't * leave the block forever, we can ignore it. */ continue; } StructuredBlock pred = skipFinExitChain(prev); if (pred instanceof JsrBlock) { JsrBlock jsr = (JsrBlock) pred; StructuredBlock jsrInner = jsr.innerBlock; if (jsrInner instanceof EmptyBlock && jsrInner.jump != null) { FlowBlock dest = jsrInner.jump.destination; if (subRoutine == null && dest.getBlockNr() >= start && dest.getNextBlockNr() <= end) { dest.analyze(start, end); if (isMonitorExitSubRoutine(dest, local)) subRoutine = dest; } if (dest == subRoutine) { /* The jump is preceeded by the right jsr. * Mark it as good. */ jsr.setGood(true); continue; } } } else if (getMonitorExitSlot(pred) == local.getSlot()) { /* The jump is preceeded by the right monitor * exit instruction. */ pred.removeBlock(); if (prev instanceof ReturnBlock) removeReturnLocal((ReturnBlock) prev); continue; } if (pred == null && isFirstJump) { /* Now we have a jump that is not preceded by a * monitorexit. There's a last chance: the jump * jumps directly to the correct monitorexit * instruction, which lies outside the try/catch * block. */ if (successor.predecessors.size() == 1 && successor.getBlockNr() >= start && successor.getNextBlockNr() <= end) { successor.analyze(start, end); StructuredBlock sb = successor.block; if (sb instanceof SequentialBlock) sb = sb.getSubBlocks()[0]; if (sb instanceof JsrBlock && sb.getSubBlocks()[0] instanceof EmptyBlock) { StructuredBlock jsrInner = sb.getSubBlocks()[0]; FlowBlock dest = jsrInner.jump.destination; if (subRoutine == null && dest.getBlockNr() >= start && dest.getNextBlockNr() <= end) { dest.analyze(start, end); if (isMonitorExitSubRoutine(dest, local)) subRoutine = dest; } if (subRoutine == dest) { successor.removeSuccessor(jsrInner.jump); jsrInner.removeJump(); sb.removeBlock(); continue dest_loop; } } if (getMonitorExitSlot(sb) == local.getSlot()) { sb.removeBlock(); continue dest_loop; } } } /* Complain! */ DescriptionBlock msg = new DescriptionBlock("ERROR: no monitorexit"); prev.appendBlock(msg); msg.moveJump(jumps); } } if (subRoutine != null) { if (tryFlow.getSuccessors().contains(subRoutine)) removeJSR(tryFlow, subRoutine); if (subRoutine.predecessors.size() == 0) tryFlow.mergeBlockNr(subRoutine); } } private StoreInstruction getExceptionStore(StructuredBlock catchBlock) { if (!(catchBlock instanceof SequentialBlock) || !(catchBlock.getSubBlocks()[0] instanceof InstructionBlock)) return null; Expression instr = ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction(); if (!(instr instanceof StoreInstruction)) return null; StoreInstruction store = (StoreInstruction) instr; if (!(store.getLValue() instanceof LocalStoreOperator && store.getSubExpressions()[1] instanceof NopOperator)) return null; return store; } private boolean analyzeSynchronized(FlowBlock tryFlow, FlowBlock catchFlow, int endHandler) { /* Check if this is a synchronized block. We mustn't change * anything until we are sure. */ StructuredBlock catchBlock = catchFlow.block; /* Check for a optional exception store and skip it */ StoreInstruction excStore = getExceptionStore(catchBlock); if (excStore != null) catchBlock = catchBlock.getSubBlocks()[1]; /* Check for the monitorexit instruction */ if (!(catchBlock instanceof SequentialBlock && catchBlock.getSubBlocks()[0] instanceof InstructionBlock)) return false; Expression instr = ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction(); if (!(instr instanceof MonitorExitOperator && instr.getFreeOperandCount() == 0 && (((MonitorExitOperator)instr).getSubExpressions()[0] instanceof LocalLoadOperator) && catchBlock.getSubBlocks()[1] instanceof ThrowBlock)) return false; /* Check for the throw instruction */ Expression throwInstr = ((ThrowBlock)catchBlock.getSubBlocks()[1]).getInstruction(); if (excStore != null) { if (!(throwInstr instanceof Operator && excStore.lvalueMatches((Operator)throwInstr))) return false; } else { if (!(throwInstr instanceof NopOperator)) return false; } /* This is a synchronized block: * * local_x = monitor object; // later * monitorenter local_x // later * tryFlow: * |- synchronized block * | ... * | every jump to outside is preceded by jsr subroutine-, * | ... | * |- monitorexit local_x | * ` jump after this block (without jsr monexit) | * | * catchBlock: | * local_n = stack | * monitorexit local_x | * throw local_n | * [OR ALTERNATIVELY:] | * monitorexit local_x | * throw stack | * optional subroutine: <-----------------------------------' * astore_n * monitorexit local_x * return_n */ /* Merge try and catch flow blocks. No need to insert the * catchFlow.block into the try flow though, since all its * instruction are synthetic. */ mergeTryCatch(tryFlow, catchFlow); MonitorExitOperator monexit = (MonitorExitOperator) ((InstructionBlock) catchBlock.getSubBlocks()[0]).instr; LocalInfo local = ((LocalLoadOperator)monexit.getSubExpressions()[0]) .getLocalInfo(); if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) GlobalOptions.err.println ("analyzeSynchronized(" + tryFlow.getBlockNr() + "," + tryFlow.getNextBlockNr() + "," + endHandler + ")"); checkAndRemoveMonitorExit (tryFlow, local, tryFlow.getNextBlockNr(), endHandler); SynchronizedBlock syncBlock = new SynchronizedBlock(local); TryBlock tryBlock = (TryBlock) tryFlow.block; syncBlock.replace(tryBlock); syncBlock.moveJump(tryBlock.jump); syncBlock.setBodyBlock(tryBlock.subBlocks.length == 1 ? tryBlock.subBlocks[0] : tryBlock); tryFlow.lastModified = syncBlock; return true; } /** * Merge try and finally flow blocks. * @param tryFlow The try flow block. Its contained block must be * a try block. * @param catchFlow The catch flow block that contains the finally * block. * @param finallyBlock block that either contains the finally block. * It is part of the catchFlow. The other parts of catchFlow are * synthetic and can be removed. */ private void mergeFinallyBlock(FlowBlock tryFlow, FlowBlock catchFlow, StructuredBlock finallyBlock) { TryBlock tryBlock = (TryBlock) tryFlow.block; if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { /* A try { try { } catch {} } finally{} is equivalent * to a try {} catch {} finally {} * so remove the surrounding tryBlock. */ TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0]; innerTry.gen = tryBlock.gen; innerTry.replace(tryBlock); tryBlock = innerTry; tryFlow.lastModified = tryBlock; tryFlow.block = tryBlock; } /* Now merge try and catch flow blocks */ mergeTryCatch(tryFlow, catchFlow); FinallyBlock newBlock = new FinallyBlock(); newBlock.setCatchBlock(finallyBlock); tryBlock.addCatchBlock(newBlock); } private boolean analyzeFinally(FlowBlock tryFlow, FlowBlock catchFlow, int end) { /* Layout of a try-finally block: * * tryFlow: * |- first instruction * | ... * | every jump to outside is preceded by jsr finally * | ... * | jsr finally -----------------, * `- jump after finally | * | * catchBlock: | * local_n = stack v * jsr finally ---------------->| * throw local_n; | * finally: <-----------------------' * astore_n * ... * return_n */ StructuredBlock catchBlock = catchFlow.block; StoreInstruction excStore = getExceptionStore(catchBlock); if (excStore == null) return false; catchBlock = catchBlock.getSubBlocks()[1]; if (!(catchBlock instanceof SequentialBlock)) return false; StructuredBlock finallyBlock = null; if (catchBlock.getSubBlocks()[0] instanceof LoopBlock) { /* In case the try block has no exit (that means, it * throws an exception or loops forever), the finallyBlock * was already merged with the catchBlock. We have to * check for this case separately: * * do { * JSR * break; * throw local_x * } while(false); * finallyBlock; (starts with POP / local_y = POP) */ LoopBlock doWhileFalse = (LoopBlock)catchBlock.getSubBlocks()[0]; if (doWhileFalse.type == LoopBlock.DOWHILE && doWhileFalse.cond == LoopBlock.FALSE && doWhileFalse.bodyBlock instanceof SequentialBlock) { if (transformSubRoutine(catchBlock.getSubBlocks()[1])) { finallyBlock = catchBlock.getSubBlocks()[1]; catchBlock = (SequentialBlock) doWhileFalse.bodyBlock; } } } if (!(catchBlock instanceof SequentialBlock && catchBlock.getSubBlocks()[0] instanceof JsrBlock && catchBlock.getSubBlocks()[1] instanceof ThrowBlock)) return false; JsrBlock jsrBlock = (JsrBlock)catchBlock.getSubBlocks()[0]; ThrowBlock throwBlock = (ThrowBlock) catchBlock.getSubBlocks()[1]; if (!(throwBlock.getInstruction() instanceof Operator && excStore.lvalueMatches((Operator) throwBlock.getInstruction()))) return false; FlowBlock subRoutine; if (finallyBlock != null) { /* Check if the jsr breaks (see comment above). We don't * need to check if it breaks to the right block, because * we know that there is only one Block around the jsr. */ if (!(jsrBlock.innerBlock instanceof BreakBlock)) return false; /* Check if the try block has no exit */ if (tryFlow.getSuccessors().size() > 0) return false; catchBlock = finallyBlock; subRoutine = null; } else { if (!(jsrBlock.innerBlock instanceof EmptyBlock)) return false; finallyBlock = jsrBlock.innerBlock; subRoutine = finallyBlock.jump.destination; /* We are committed now and can start changing the try * block. */ checkAndRemoveJSR(tryFlow, subRoutine, tryFlow.getNextBlockNr(), end); /* Now analyze and transform the subroutine. */ while (subRoutine.analyze(tryFlow.getNextBlockNr(), end)); if (subRoutine.predecessors.size() == 1) { /* catchFlow is synthetic, so we can safely remove it * here. */ subRoutine.mergeBlockNr(catchFlow); catchFlow = subRoutine; if (!transformSubRoutine(subRoutine.block)) { finallyBlock = subRoutine.block; DescriptionBlock msg = new DescriptionBlock ("ERROR: Missing return address handling"); msg.replace(finallyBlock); msg.appendBlock(finallyBlock); } finallyBlock = subRoutine.block; } } /* Now finish it. */ mergeFinallyBlock(tryFlow, catchFlow, finallyBlock); return true; } private boolean analyzeSpecialFinally(FlowBlock tryFlow, FlowBlock catchFlow, int end) { StructuredBlock finallyBlock = catchFlow.block; StructuredBlock firstInstr = finallyBlock instanceof SequentialBlock ? finallyBlock.getSubBlocks()[0]: finallyBlock; if (!(firstInstr instanceof SpecialBlock && ((SpecialBlock)firstInstr).type == SpecialBlock.POP && ((SpecialBlock)firstInstr).count == 1)) return false; /* This is a special try/finally-block, where * the finally block ends with a break, return or * similar. */ /* Make sure that resolveJump only works on the inside of the try */ tryFlow.lastModified = tryFlow.block.getSubBlocks()[0]; if (finallyBlock instanceof SequentialBlock) finallyBlock = finallyBlock.getSubBlocks()[1]; else { finallyBlock = new EmptyBlock(); finallyBlock.moveJump(firstInstr.jump); /* Handle the jumps in the tryFlow to finallyFlow. */ FlowBlock finallyFlow = finallyBlock.jump.destination; if (tryFlow.getSuccessors().contains(finallyFlow)) { Jump jumps = tryFlow.removeJumps(finallyFlow); jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow); tryFlow.resolveRemaining(jumps); } } /* Complain about all other jumps in try block */ Set trySuccs = tryFlow.getSuccessors(); for (Iterator i = trySuccs.iterator(); i.hasNext(); ) { for (Jump jumps = tryFlow.getJumps((FlowBlock) i.next()); jumps != null; jumps = jumps.next) { DescriptionBlock msg = new DescriptionBlock ("ERROR: doesn't go through finally block!"); if (jumps.prev instanceof ReturnBlock) { msg.replace(jumps.prev); msg.appendBlock(jumps.prev); } else { jumps.prev.appendBlock(msg); msg.moveJump(jumps); } } } mergeFinallyBlock(tryFlow, catchFlow, finallyBlock); /* Following code will work be put inside the finallyBlock */ tryFlow.lastModified = finallyBlock; return true; } void checkTryCatchOrder() { /* Check if try/catch ranges are okay. The following succeeds * for all classes generated by the sun java compiler, but hand * optimized classes (or generated by other compilers) will fail. */ Handler last = null; for (Iterator i = handlers.iterator(); i.hasNext(); ) { Handler exc = (Handler) i.next(); int start = exc.start.getBlockNr(); int end = exc.end.getBlockNr(); int handler = exc.handler.getBlockNr(); if (start > end || handler <= end) throw new InternalError ("ExceptionHandler order failed: not " + start + " < " + end + " <= " + handler); if (last != null && (last.start.getBlockNr() != start || last.end.getBlockNr() != end)) { /* The last handler does catch another range. * Due to the order: * start < last.start.getBlockNr() * || end > last.end.getBlockNr() */ if (end >= last.start.getBlockNr() && end < last.end.getBlockNr()) throw new InternalError ("Exception handlers ranges are intersecting: [" + last.start.getBlockNr()+", " + last.end.getBlockNr()+"] and [" + start+", "+end+"]."); } last = exc; } } /** * Analyzes all exception handlers to try/catch/finally or * synchronized blocks. */ public void analyze() { checkTryCatchOrder(); Iterator i = handlers.iterator(); Handler exc = null; Handler next = i.hasNext() ? (Handler) i.next() : null; while(next != null) { Handler last = exc; exc = next; next = i.hasNext() ? (Handler) i.next() : null; int startNr = exc.start.getBlockNr(); int endNr = exc.end.getBlockNr(); int endHandler = Integer.MAX_VALUE; /* If the next exception handler catches a bigger range * it must surround the handler completely. */ if (next != null && next.end.getBlockNr() > endNr) endHandler = next.end.getBlockNr() + 1; FlowBlock tryFlow = exc.start; tryFlow.checkConsistent(); if (last == null || exc.type == null || last.start.getBlockNr() != startNr || last.end.getBlockNr() != endNr) { /* The last handler does catch another range. * Create a new try block. */ if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) GlobalOptions.err.println ("analyzeTry(" + startNr + ", " + endNr+")"); while(true) { while (tryFlow.analyze(startNr, endNr+1)); int nextNr = tryFlow.getNextBlockNr(); if (nextNr > endNr) break; tryFlow = flowBlocks[nextNr]; } if (tryFlow.getBlockNr() != startNr) { GlobalOptions.err.println ("Warning: Can't completely analyze try."); } new TryBlock(tryFlow); } else if (!(tryFlow.block instanceof TryBlock)) throw new InternalError("no TryBlock"); FlowBlock catchFlow = exc.handler; boolean isMultiUsed = catchFlow.predecessors.size() != 0; if (!isMultiUsed && next != null) { for (Iterator j = handlers.tailSet(next).iterator(); j.hasNext();) { Handler h = (Handler) j.next(); if (h.handler == catchFlow) { isMultiUsed = true; break; } } } if (isMultiUsed) { /* If this exception is used in other exception handlers, * create a new flow block, that jumps to the handler. * This will be our new exception handler. */ FlowBlock newFlow = new FlowBlock (catchFlow.method, catchFlow.getBlockNr(), catchFlow.prevByCodeOrder); newFlow.setSuccessors(new FlowBlock[] { catchFlow }); newFlow.nextByCodeOrder = catchFlow; catchFlow.prevByCodeOrder = newFlow; catchFlow = newFlow; } else { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) GlobalOptions.err.println ("analyzeCatch(" + catchFlow.getBlockNr() + ", " + endHandler + ")"); while (catchFlow.analyze(catchFlow.getBlockNr(), endHandler)); } if (exc.type != null) analyzeCatchBlock(exc.type, tryFlow, catchFlow); else if (! analyzeSynchronized(tryFlow, catchFlow, endHandler) && ! analyzeFinally(tryFlow, catchFlow, endHandler) && ! analyzeSpecialFinally(tryFlow, catchFlow, endHandler)) /* As last resort make a catch(Object) block. This doesn't * compile, but at least it gives a hint what the code * does. */ analyzeCatchBlock(Type.tObject, tryFlow, catchFlow); tryFlow.checkConsistent(); if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) GlobalOptions.err.println ("analyzeTryCatch(" + tryFlow.getBlockNr() + ", " + tryFlow.getNextBlockNr() + ") done."); } } }