diff --git a/jode/jode/flow/TransformExceptionHandlers.java b/jode/jode/flow/TransformExceptionHandlers.java index 62ccb3d..544b1ab 100644 --- a/jode/jode/flow/TransformExceptionHandlers.java +++ b/jode/jode/flow/TransformExceptionHandlers.java @@ -30,15 +30,13 @@ import java.util.Enumeration; * @author Jochen Hoenicke */ public class TransformExceptionHandlers { - FlowBlock[] flows; int count; - int[] startPCs = new int[4]; + FlowBlock[] trys = new FlowBlock[4]; + FlowBlock[] catches = new FlowBlock[4]; int[] endPCs = new int[4]; - int[] handlerPCs = new int[4]; Type[] types = new Type[4]; - public TransformExceptionHandlers(FlowBlock[] flowBlocks) { - flows = flowBlocks; + public TransformExceptionHandlers() { count = 0; } @@ -49,63 +47,66 @@ public class TransformExceptionHandlers { * @param handler The address of the handler. * @param type The type of the exception, null for ALL. */ - public void addHandler(int start, int end, int handler, Type type) { + public void addHandler(FlowBlock tryBlock, int end, + FlowBlock catchBlock, Type type) { int offset = 0; + int start = tryBlock.addr; + int handler = catchBlock.addr; /* First sort by start offsets, highest address first...*/ - while (offset < count && start < startPCs[offset]) + while (offset < count && start < trys[offset].addr) offset++; /* ...Second sort by end offsets, lowest address first... * this will move the innermost blocks to the beginning. */ - while (offset < count && start == startPCs[offset] + while (offset < count && start == trys[offset].addr && end > endPCs[offset]) offset++; /* ...Last sort by handler offsets, lowest first */ - while (offset < count && start == startPCs[offset] - && end == endPCs[offset] && handler > handlerPCs[offset]) + while (offset < count && start == trys[offset].addr + && end == endPCs[offset] && handler > catches[offset].addr) offset++; - if (count++ >= startPCs.length) { + if (count++ >= trys.length) { /* We grow the arrays by 50 % */ - int newSize = startPCs.length * 3 / 2; - int[] newStartPCs = new int[newSize]; + int newSize = trys.length * 3 / 2; + FlowBlock[] newTrys = new FlowBlock[newSize]; int[] newEndPCs = new int[newSize]; - int[] newHandlerPCs = new int[newSize]; + FlowBlock[] newCatches = new FlowBlock[newSize]; Type[] newTypes = new Type[newSize]; - System.arraycopy(startPCs, 0, newStartPCs, 0, offset); + System.arraycopy(trys, 0, newTrys, 0, offset); System.arraycopy(endPCs, 0, newEndPCs, 0, offset); - System.arraycopy(handlerPCs, 0, newHandlerPCs, 0, offset); + System.arraycopy(catches, 0, newCatches, 0, offset); System.arraycopy(types, 0, newTypes, 0, offset); if (offset+1 < count) { - System.arraycopy(startPCs, offset, newStartPCs, offset+1, + System.arraycopy(trys, offset, newTrys, offset+1, count-offset-1); System.arraycopy(endPCs, offset, newEndPCs, offset+1, count-offset-1); - System.arraycopy(handlerPCs, offset, newHandlerPCs, offset+1, + System.arraycopy(catches, offset, newCatches, offset+1, count-offset-1); System.arraycopy(types, offset, newTypes, offset+1, count-offset-1); } - startPCs = newStartPCs; + trys = newTrys; endPCs = newEndPCs; - handlerPCs = newHandlerPCs; + catches = newCatches; types = newTypes; } else if (offset+1 < count) { /* Move the tailing data one place below */ - System.arraycopy(startPCs, offset, startPCs, offset+1, + System.arraycopy(trys, offset, trys, offset+1, count-offset-1); System.arraycopy(endPCs, offset, endPCs, offset+1, count-offset-1); - System.arraycopy(handlerPCs, offset, handlerPCs, offset+1, + System.arraycopy(catches, offset, catches, offset+1, count-offset-1); System.arraycopy(types, offset, types, offset+1, count-offset-1); } /* Insert the new handler */ - startPCs[offset] = start; + trys[offset] = tryBlock; endPCs[offset] = end; - handlerPCs[offset] = handler; + catches[offset] = catchBlock; types[offset] = type; } @@ -114,7 +115,17 @@ public class TransformExceptionHandlers { * successing flow block for a try catch block. The main difference * to updateInOut in FlowBlock is, that this function works, as if * every instruction would have a jump. This is because every - * instruction can throw an exception and thus enter the catch block. + * instruction can throw an exception and thus enter the catch block.
+ * + * For example this code prints 0: + *
+     *   int a=3;
+     *   try {
+     *     a = 5 / (a=0);
+     *   } catch (DivideByZeroException ex) {
+     *     System.err.println(a);
+     *   }
+     * 
* * @param successor The flow block which is unified with this flow * block. @@ -166,17 +177,18 @@ public class TransformExceptionHandlers { StructuredBlock firstInstr = (catchBlock instanceof SequentialBlock) ? catchBlock.getSubBlocks()[0] : catchBlock; - if (firstInstr instanceof InstructionBlock) { + if (firstInstr instanceof SpecialBlock + && ((SpecialBlock) firstInstr).type == SpecialBlock.POP + && ((SpecialBlock) firstInstr).count == 1) { + /* The exception is ignored. Create a dummy local for it */ + local = new LocalInfo(-1); + local.setName("exception"); + firstInstr.removeBlock(); + + } else if (firstInstr instanceof InstructionBlock) { Expression instr = ((InstructionBlock) firstInstr).getInstruction(); - if (instr instanceof PopOperator - && ((PopOperator) instr).getCount() == 1) { - /* The exception is ignored. Create a dummy local for it */ - local = new LocalInfo(-1); - local.setName("exception"); - firstInstr.removeBlock(); - - } else if (instr instanceof LocalStoreOperator) { + if (instr instanceof LocalStoreOperator) { /* The exception is stored in a local variable */ local = ((LocalStoreOperator) instr).getLocalInfo(); firstInstr.removeBlock(); @@ -193,7 +205,7 @@ public class TransformExceptionHandlers { ((TryBlock)tryFlow.block).addCatchBlock(newBlock); newBlock.setCatchBlock(catchFlow.block); tryFlow.mergeSuccessors(catchFlow); - tryFlow.length += catchFlow.length; + tryFlow.mergeAddr(catchFlow); } /* And now the complicated parts. */ @@ -216,6 +228,18 @@ public class TransformExceptionHandlers { while (sequBlock.subBlocks[1] instanceof SequentialBlock) sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; + /* XXX - Check that the local isn't used for any other purposes + * than RET and replace any RET with a flow control to end of + * flow block. + * + * This is a complicated task which isn't needed for javac nor jikes. + */ + if (sequBlock.subBlocks[1].jump != null + && sequBlock.subBlocks[1].jump.destination + == FlowBlock.END_OF_METHOD) { + instr.removeBlock(); + return true; + } if (! (sequBlock.subBlocks[1] instanceof RetBlock) || !(((RetBlock)sequBlock.subBlocks[1]) .local.equals(store.getLocalInfo()))) @@ -465,7 +489,7 @@ public class TransformExceptionHandlers { ((InstructionBlock)subRoutine.block) .getInstruction(); if (isMonitorExit(instr, local)) { - tryFlow.length += subRoutine.length; + tryFlow.mergeAddr(subRoutine); continue dest_loop; } } @@ -540,10 +564,10 @@ public class TransformExceptionHandlers { LocalInfo local = ((LocalLoadOperator)monexit.getSubExpressions()[0]) .getLocalInfo(); - tryFlow.length += catchFlow.length; + tryFlow.mergeAddr(catchFlow); checkAndRemoveMonitorExit - (tryFlow, local, catchFlow.addr+catchFlow.length, endHandler); + (tryFlow, local, catchFlow.getNextAddr(), endHandler); SynchronizedBlock syncBlock = new SynchronizedBlock(local); TryBlock tryBlock = (TryBlock) tryFlow.block; @@ -613,7 +637,7 @@ public class TransformExceptionHandlers { catchBlock = (SequentialBlock) doWhileFalse.bodyBlock; } } - + if (catchBlock instanceof SequentialBlock && catchBlock.getSubBlocks()[0] instanceof JsrBlock && instr instanceof LocalStoreOperator @@ -667,7 +691,7 @@ public class TransformExceptionHandlers { transformSubRoutine(finallyBlock); updateInOutCatch(tryFlow, catchFlow); - tryFlow.length += catchFlow.length; + tryFlow.mergeAddr(catchFlow); finallyBlock = catchFlow.block; tryFlow.mergeSuccessors(catchFlow); @@ -676,16 +700,16 @@ public class TransformExceptionHandlers { ((JsrBlock)catchBlock.getSubBlocks()[0]) .innerBlock.jump.destination; - subRoutine.analyze(catchFlow.addr+catchFlow.length, end); + subRoutine.analyze(catchFlow.getNextAddr(), end); if (!transformSubRoutine(subRoutine.block)) return false; - - tryFlow.length += catchFlow.length; + + tryFlow.mergeAddr(catchFlow); checkAndRemoveJSR(tryFlow, subRoutine); updateInOutCatch(tryFlow, subRoutine); - tryFlow.length += subRoutine.length; + tryFlow.mergeAddr(subRoutine); tryFlow.mergeSuccessors(subRoutine); finallyBlock = subRoutine.block; @@ -722,11 +746,9 @@ public class TransformExceptionHandlers { catchFlow.block instanceof SequentialBlock ? catchFlow.block.getSubBlocks()[0]: catchFlow.block; - if (firstInstr instanceof InstructionBlock - && ((InstructionBlock) firstInstr).getInstruction() - instanceof PopOperator - && ((PopOperator) ((InstructionBlock) firstInstr) - .getInstruction()).getCount() == 1) { + if (firstInstr instanceof SpecialBlock + && ((SpecialBlock)firstInstr).type == SpecialBlock.POP + && ((SpecialBlock)firstInstr).count == 1) { /* This is a special try/finally-block, where * the finally block ends with a break, return or @@ -756,7 +778,7 @@ public class TransformExceptionHandlers { /* remove the pop now */ firstInstr.removeBlock(); - tryFlow.length += catchFlow.length; + tryFlow.mergeAddr(catchFlow); if (succ != null) { Jump jumps = (Jump) tryFlow.successors.remove(succ); @@ -781,8 +803,8 @@ public class TransformExceptionHandlers { /* try block has no successors */ if (succ != null && succ.predecessors.size() == 1) { - while (succ.analyze(catchFlow.addr+catchFlow.length, end)); - tryFlow.length += succ.length; + while (succ.analyze(catchFlow.getNextAddr(), end)); + tryFlow.mergeAddr(succ); tryFlow.successors.remove(succ); newBlock.setCatchBlock(succ.block); tryFlow.mergeSuccessors(succ); @@ -804,43 +826,43 @@ public class TransformExceptionHandlers { public void analyze() { /* Check if try/catch ranges are okay. The following succeeds * for all classes generated by the sun java compiler, but hand - * optimized classes will fail this. + * optimized classes (or generated by other compilers) will fail. */ for (int i=0; i= end || handler < end) throw new AssertError("ExceptionHandler order failed: not " + start + " < " + end + " <= " + handler); if (i == 0 - || startPCs[i-1] != start || endPCs[i-1] != end) { + || trys[i-1].addr != start || endPCs[i-1] != end) { /* The last handler does catch another range. */ if ( /*handler > end + 1 || */ - (i > 0 && end > startPCs[i-1] && end < endPCs[i-1])) + (i > 0 && end > trys[i-1].addr && end < endPCs[i-1])) throw new AssertError("ExceptionHandler" + " at wrong position: " + "end: "+end + " handler: "+handler - + (i>0 ? " last: ("+startPCs[i-1] + + (i>0 ? " last: ("+trys[i-1].addr +", "+endPCs[i-1]+", " - +handlerPCs[i-1]+")" + +catches[i-1].addr+")" :"")); } } for (int i=0; i handlerPCs[i]) + int endHandler = (i< count-1 && endPCs[i+1] > catches[i].addr) ? endPCs[i+1] : Integer.MAX_VALUE; if (Decompiler.debugAnalyze) - Decompiler.err.println("analyzeCatch(" + startPCs[i] + ", " - + endPCs[i] + ", " +handlerPCs[i] + ")"); - FlowBlock tryFlow = flows[startPCs[i]]; - while (tryFlow.analyze(startPCs[i], handlerPCs[i])); + Decompiler.err.println("analyzeCatch(" + trys[i].addr + ", " + + endPCs[i] + ", " +catches[i].addr + ")"); + FlowBlock tryFlow = trys[i]; + while (tryFlow.analyze(tryFlow.addr, catches[i].addr)); if (i == 0 - || startPCs[i-1] != startPCs[i] || endPCs[i-1] != endPCs[i]) { + || trys[i-1].addr != trys[i].addr || endPCs[i-1] != endPCs[i]) { /* The last handler does catch another range. * Create a new try block. */ @@ -848,19 +870,8 @@ public class TransformExceptionHandlers { } else if (! (tryFlow.block instanceof TryBlock)) throw new AssertError("no TryBlock"); - FlowBlock catchFlow = flows[handlerPCs[i]]; - while (catchFlow.analyze(handlerPCs[i], endHandler)); - - if (!catchFlow.predecessors.isEmpty()) { - /* This can actually happen, namely in code compiled by - * jikes. In this case the predecessor is a nop and has - * no further predecessors. - */ - if (catchFlow.predecessors.size() != 1 - && ((FlowBlock)catchFlow.predecessors.elementAt(0)) - .predecessors.size() != 0) - throw new AssertError("Handler has a predecessors"); - } + FlowBlock catchFlow = catches[i]; + while (catchFlow.analyze(catchFlow.addr, endHandler)); updateInOutCatch(tryFlow, catchFlow); if (types[i] != null)