diff --git a/jode/ChangeLog b/jode/ChangeLog index 7b158d0..0121658 100644 --- a/jode/ChangeLog +++ b/jode/ChangeLog @@ -1,3 +1,35 @@ +2001-08-12 Jochen Hoenicke + + * jode/flow/FlowBlock.java.in: + (checkConsistent): Allow lastModified in a finally block. + + * jode/flow/TransformExceptionHandlers.java.in: Reworked exception + handlers again. This time checked with javac 1.3, javac 1.1 and + jikes. + (checkTryCatchOrder): New method that was previously part of + analyze. + (analyze): Use checkTryCatchOrder. Don't merge try and catch flow + blocks anymore, leave it to the analyzeXXX methods. + (mergeTryCatch): New method. + (analyzeCatchBlock): Get catchFlow as parameter. Call + mergeTryCatch. + (transformSubroutine): Handle POP-only subroutines. + (removeJSR): Don't do special case for catchBlock any more. This + is because catchFlow isn't yet merged when this method is called. + (checkAndRemoveJSR): Likewise. + (checkAndRemoveMonitorExit): Likewise. Merge subroutine only if + we are the only predecessor. + (analyzeSynchronized): Get catchFlow as parameter. Call + mergeTryCatch. + (mergeFinallyBlocks): New method, calls mergeTryCatch and does the + common part of mergeFinally and mergeSpecialFinally. + (analyzeFinally): Simplified, after checking and removing JSR, it + does immediately analyze and transform subroutine to get the + finallyBlock. Then it throws away the catchFlow and calls + mergeFinallyBlocks. + (analyzeSpecialFinally): Simplified, after checking it only handles + the jumps in the try part and then call mergeFinallyBlocks. + 2001-08-10 Jochen Hoenicke * configure.in: Changed bash syntax to more compatible (but @@ -23,7 +55,7 @@ * jode/obfuscator/modules/SimpleAnalyzer.java.in: Ported fix from ConstantAnalyzer: - (canonizeReference): for interfaces call canonizeIfaceReference. + (canonizeReference): for interfaces call canonizeIfaceReference. (canonizeIfaceReference): new method. * jode/obfuscator/modules/ConstantAnalyzer.java.in: made sub class of SimpleAnalyzer. @@ -32,8 +64,7 @@ 2001-07-08 Jochen Hoenicke - * jode/bytecode/BytecodeInfo.java.in (calculateMaxStack): Handle - special case for empty method. Previous code would just crash. + * jode/bytecode/BytecodeInfo.java.in (calculateMaxStack): Handle special case for empty method. Previous code would just crash. 2001-06-15 Jochen Hoenicke @@ -166,8 +197,8 @@ 2001-05-08 Jochen Hoenicke * jode/jvm/CodeVerifier.java.in (doVerify): Don't check for - uninitialized objects in local or stack slots on backwards jump or - exception blocks. Sun's jdk also doesn't check it, and I never + uninitialized objects in local or stack slots on backwards jump or + exception blocks. Sun's jdk also doesn't check it, and I never understood why it is necessary. But see JVM Spec 4.9.4. 2001-05-02 Jochen Hoenicke @@ -225,7 +256,7 @@ 2001-02-08 Jochen Hoenicke * jode/expr/StoreInstruction.java (dumpExpression): Java doesn't - allow parenthesis around left hand side, so use NO_PAREN and don't + allow parenthesis around left hand side, so use NO_PAREN and don't call lhs.dumpExpression() with priority. * jode/expr/PrePostFixOperator.java (dumpExpression): likewise. * jode/expr/IIncOperator.java (dumpExpression): likewise. @@ -243,7 +274,7 @@ ref.getClazz() gives a type signature. * jode/flow/TransformExceptionHandlers.java.in: (checkAndRemoveJSR): Only invoke removeBadJSR, if there are - successors left. + successors left. (checkAndRemoveMonitorExit): likewise. 2001-02-04 Jochen Hoenicke @@ -321,14 +352,14 @@ * jode/decompiler/Options.java (GNU_SPACING): new constant. (GNU_STYLE): changed to include GNU_SPACING. * jode/decompiler/ClassAnalyzer.java.in (dumpSource): Use - open/closeBraceClass. + open/closeBraceClass. * jode/decompiler/MethodAnalyzer.java.in (dumpSource): Use - open/closeBraceNoIndent. Insert a space for GNU_SPACING. + open/closeBraceNoIndent. Insert a space for GNU_SPACING. * jode/decompiler/InvokeOperator.java.in (dumpExpression): Insert - a space for GNU_SPACING, use open/closeBraceClass for inner - classes. + a space for GNU_SPACING, use open/closeBraceClass for inner + classes. * jode/decompiler/UnaryOperator.java (dumpExpression): Insert - a space for GNU_SPACING. + a space for GNU_SPACING. 2001-01-30 Jochen Hoenicke @@ -338,30 +369,30 @@ * jode/decompiler/Decompiler.java (setOption): detect pascal option. * jode/decompiler/Main.java (main): dito. * jode/decompiler/TabbedPrintWriter.java (openBrace, - openBraceContinue, closeBrace, closeBraceNoSpace, - closeBraceContinue): handle flush left. + openBraceContinue, closeBrace, closeBraceNoSpace, + closeBraceContinue): handle flush left. 2001-01-30 Jochen Hoenicke * jode/type/NullType.java (intersection): Removed, since the - version in ReferenceType is more correct. Before - tNull.isOfType(tRange(X,tNull)) returned false, which lead to - incorrect behaviour in InvokeOperator.needsCast. + version in ReferenceType is more correct. Before + tNull.isOfType(tRange(X,tNull)) returned false, which lead to + incorrect behaviour in InvokeOperator.needsCast. * jode/decompiler/FieldAnalyzer.java.in (dumpSource): Removed the - "= null" hack for final fields; it was not correct, since the - field could be initialized in a constructor. + "= null" hack for final fields; it was not correct, since the + field could be initialized in a constructor. * jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp): Simplified the code, copy options always from child. * jode/jvm/SyntheticAnalyzer.java.in (unifyParam): new field. (checkConstructorAccess): Allow the special Parameter, whose - purpose is to distinguish the wrapper from the real constructor - and give him a "$" in the type signature, to appear at every - position. It doesn't appear at position 1 for inner classes. + purpose is to distinguish the wrapper from the real constructor + and give him a "$" in the type signature, to appear at every + position. It doesn't appear at position 1 for inner classes. Store the position in unifyParam. * jode/expr/InvokeOperator.java (isGetClass): Allow the method to - be declared inside an outer class: We simply check if we can get + be declared inside an outer class: We simply check if we can get the method analyzer. (simplify): handle unifyParam. * jode/expr/PopOperator.java (getBreakPenalty): return penalty of - inner expression. (dumpExpression): Call dumpExpression of - subexpression immediately without priority. + inner expression. (dumpExpression): Call dumpExpression of + subexpression immediately without priority. diff --git a/jode/jode/flow/FlowBlock.java.in b/jode/jode/flow/FlowBlock.java.in index 1fe6409..0f69a97 100644 --- a/jode/jode/flow/FlowBlock.java.in +++ b/jode/jode/flow/FlowBlock.java.in @@ -1,4 +1,4 @@ -/* FlowBlock Copyright (C) 1998-1999 Jochen Hoenicke. +/* FlowBlock Copyright (C) 1998-2001 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -748,7 +748,8 @@ public class FlowBlock { StructuredBlock last = lastModified; while (last.outer instanceof SequentialBlock - || last.outer instanceof TryBlock) + || last.outer instanceof TryBlock + || last.outer instanceof FinallyBlock) last = last.outer; if (last.outer != null) throw new AssertError("Inconsistency"); diff --git a/jode/jode/flow/TransformExceptionHandlers.java.in b/jode/jode/flow/TransformExceptionHandlers.java.in index 17f2e14..2358c86 100644 --- a/jode/jode/flow/TransformExceptionHandlers.java.in +++ b/jode/jode/flow/TransformExceptionHandlers.java.in @@ -99,35 +99,63 @@ public class TransformExceptionHandlers { } - /* simple try catch block: + /** + * 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.getAddr() + + ", " + catchFlow.getAddr() + ")"); + tryFlow.updateInOutCatch(catchFlow); + tryFlow.mergeSuccessors(catchFlow); + tryFlow.mergeAddr(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. * - * try-header - * |- first instruction - * | ... - * | last instruction - * |- optional jump (last+1) - * | ... - * `- catch block + * @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, - StructuredBlock catchBlock) { + 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(catchBlock); + newBlock.setCatchBlock(catchFlow.block); } /* And now the complicated parts. */ /** - * This transforms a sub routine, that is checks if the beginning - * local assignment matches the final ret and then returns. + * 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 subRoutine) { - if (!(subRoutine instanceof SequentialBlock)) - return false; - SequentialBlock sequBlock = (SequentialBlock) subRoutine; - StructuredBlock firstBlock = sequBlock.getSubBlocks()[0]; + boolean transformSubRoutine(StructuredBlock subRoutineBlock) { + StructuredBlock firstBlock = subRoutineBlock; + if (firstBlock instanceof SequentialBlock) + firstBlock = subRoutineBlock.getSubBlocks()[0]; LocalInfo local = null; if (firstBlock instanceof SpecialBlock) { @@ -152,24 +180,25 @@ public class TransformExceptionHandlers { } else return false; - /* We have now committed. Remove the first Statement which - * stores/removes the return address. + /* We are now committed and can start changing code. Remove + * the first Statement which stores/removes the return + * address. */ firstBlock.removeBlock(); - - /* XXX - Replace any RET with a jump to end of this flow block. + + /* 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 - * replace this. This will never produce code with wrong semantic, + * remove this. This will never produce code with wrong semantic, * as long as the bytecode was verified correctly. */ - while (sequBlock.subBlocks[1] instanceof SequentialBlock) - sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; + while (subRoutineBlock instanceof SequentialBlock) + subRoutineBlock = subRoutineBlock.getSubBlocks()[1]; - if (sequBlock.subBlocks[1] instanceof RetBlock - && (((RetBlock) sequBlock.subBlocks[1]).local.equals(local))) { - sequBlock.subBlocks[1].removeBlock(); + if (subRoutineBlock instanceof RetBlock + && (((RetBlock) subRoutineBlock).local.equals(local))) { + subRoutineBlock.removeBlock(); } return true; } @@ -199,51 +228,32 @@ public class TransformExceptionHandlers { } /** - * Remove the wrongly placed JSRs jumping to the specified - * subRoutine. The right JSRs are already removed, but we have to - * replace the wrong ones with a warning. - * @param tryFlow the FlowBLock of the try block. + * 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, StructuredBlock catchBlock, - FlowBlock subRoutine) { - Jump nextJump; - for (Jump jumps = tryFlow.getJumps(subRoutine); - jumps != null; jumps = nextJump) { + private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) { + for (Jump jumps = tryFlow.removeJumps(subRoutine); + jumps != null; jumps = jumps.next) { StructuredBlock prev = jumps.prev; - nextJump = jumps.next; - if (prev instanceof EmptyBlock - && prev.outer instanceof JsrBlock) { - JsrBlock jsr = (JsrBlock) prev.outer; - if (prev.outer == catchBlock) { - /* This is the mandatory jsr in the catch block */ - continue; - } + prev.removeJump(); - tryFlow.removeSuccessor(jumps); - prev.removeJump(); - if (jsr.isGood()) { - StructuredBlock next = jsr.getNextBlock(); - jsr.removeBlock(); - if (next instanceof ReturnBlock) - removeReturnLocal((ReturnBlock) next); - } else { - /* We have a JSR to the subroutine, which is badly placed. - * We complain here. - */ - DescriptionBlock msg - = new DescriptionBlock("ERROR: JSR FINALLY BLOCK!"); - msg.replace(prev.outer); - } - } else { - /* We have a jump to the subroutine, that is wrong. + 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: GOTO FINALLY BLOCK!"); - tryFlow.removeSuccessor(jumps); - prev.removeJump(); + DescriptionBlock msg = new DescriptionBlock + ("ERROR: invalid jump to finally block!"); prev.appendBlock(msg); } } @@ -309,7 +319,6 @@ public class TransformExceptionHandlers { private void checkAndRemoveJSR(FlowBlock tryFlow, - StructuredBlock catchBlock, FlowBlock subRoutine, int startOutExit, int endOutExit) { Iterator iter = tryFlow.getSuccessors().iterator(); @@ -332,8 +341,8 @@ public class TransformExceptionHandlers { } 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. + /* This jump is a jsr, since it doesn't leave the + * block forever, we can ignore it. */ continue; } @@ -384,7 +393,7 @@ public class TransformExceptionHandlers { * Complain! */ DescriptionBlock msg - = new DescriptionBlock("ERROR: NO JSR TO FINALLY"); + = new DescriptionBlock("ERROR: no jsr to finally"); if (pred != null) pred.prependBlock(msg); else { @@ -394,11 +403,10 @@ public class TransformExceptionHandlers { } } if (tryFlow.getSuccessors().contains(subRoutine)) - removeJSR(tryFlow, catchBlock, subRoutine); + removeJSR(tryFlow, subRoutine); } private void checkAndRemoveMonitorExit(FlowBlock tryFlow, - StructuredBlock catchBlock, LocalInfo local, int start, int end) { FlowBlock subRoutine = null; @@ -502,7 +510,7 @@ public class TransformExceptionHandlers { /* Complain! */ DescriptionBlock msg - = new DescriptionBlock("ERROR: NO MONITOREXIT"); + = new DescriptionBlock("ERROR: no monitorexit"); prev.appendBlock(msg); msg.moveJump(jumps); } @@ -510,8 +518,9 @@ public class TransformExceptionHandlers { if (subRoutine != null) { if (tryFlow.getSuccessors().contains(subRoutine)) - removeJSR(tryFlow, catchBlock, subRoutine); - tryFlow.mergeAddr(subRoutine); + removeJSR(tryFlow, subRoutine); + if (subRoutine.predecessors.size() == 0) + tryFlow.mergeAddr(subRoutine); } } @@ -534,20 +543,25 @@ public class TransformExceptionHandlers { } private boolean analyzeSynchronized(FlowBlock tryFlow, - StructuredBlock catchBlock, + 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] @@ -591,6 +605,16 @@ public class TransformExceptionHandlers { * 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. + * + * Though we need to remove the jump of the throw + * instruction. + */ + catchFlow.removeSuccessor(catchBlock.getSubBlocks()[1].jump); + mergeTryCatch(tryFlow, catchFlow); MonitorExitOperator monexit = (MonitorExitOperator) ((InstructionBlock) catchBlock.getSubBlocks()[0]).instr; @@ -605,7 +629,7 @@ public class TransformExceptionHandlers { + "," + tryFlow.getNextAddr() + "," + endHandler + ")"); checkAndRemoveMonitorExit - (tryFlow, catchBlock, local, tryFlow.getNextAddr(), endHandler); + (tryFlow, local, tryFlow.getNextAddr(), endHandler); SynchronizedBlock syncBlock = new SynchronizedBlock(local); TryBlock tryBlock = (TryBlock) tryFlow.block; @@ -617,8 +641,41 @@ public class TransformExceptionHandlers { 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, - StructuredBlock catchBlock, int end) { + FlowBlock catchFlow, int end) { /* Layout of a try-finally block: * @@ -640,6 +697,7 @@ public class TransformExceptionHandlers { * return_n */ + StructuredBlock catchBlock = catchFlow.block; StoreInstruction excStore = getExceptionStore(catchBlock); if (excStore == null) return false; @@ -651,9 +709,10 @@ public class TransformExceptionHandlers { StructuredBlock finallyBlock = null; if (catchBlock.getSubBlocks()[0] instanceof LoopBlock) { - /* In case the try block has no exit (that means, it throws - * an exception), the finallyBlock was already merged with - * the catchBlock. We have to check for this case separately: + /* 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 @@ -690,262 +749,270 @@ public class TransformExceptionHandlers { FlowBlock subRoutine; if (finallyBlock != null) { - /* Check if the jsr breaks (see two comments above). We don't + /* 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 (except throws) - * XXX - Unfortunately the try block already has the - * successors of catch block. + /* TODO - Check if the try block has no exit (except throws) */ -// if (tryFlow.getSuccessors() > 0) +// if (tryFlow.getSuccessors().size() > 0) // return false; - + catchBlock = finallyBlock; subRoutine = null; + catchFlow.removeSuccessor(throwBlock.jump); } else { if (!(jsrBlock.innerBlock instanceof EmptyBlock)) return false; - catchBlock = jsrBlock; - subRoutine = jsrBlock.innerBlock.jump.destination; - checkAndRemoveJSR(tryFlow, catchBlock, subRoutine, - tryFlow.getNextAddr(), subRoutine.getAddr()); - } - - /* Wow that was complicated :-) - * But now we know that the catch block looks - * exactly like it should: - * - * local_n = POP - * catchBlock: - * JSR - * finally - * throw local_n - */ - - TryBlock tryBlock = (TryBlock) tryFlow.block; - if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { - /* 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; - } - FinallyBlock newBlock = new FinallyBlock(); - newBlock.setCatchBlock(catchBlock); - tryBlock.addCatchBlock(newBlock); + finallyBlock = jsrBlock.innerBlock; + subRoutine = finallyBlock.jump.destination; - if (subRoutine != null) { - while (subRoutine.analyze(tryFlow.getNextAddr(), end)); - - /* Now check if the subroutine is correct and has only the - * catchFlow as predecessor. + /* We are committed now and can start changing the blocks. */ - if (subRoutine.predecessors.size() == 1 - && transformSubRoutine(subRoutine.block)) { + catchFlow.removeSuccessor(throwBlock.jump); + checkAndRemoveJSR(tryFlow, subRoutine, + tryFlow.getNextAddr(), end); - tryFlow.removeSuccessor(jsrBlock.innerBlock.jump); - tryFlow.mergeAddr(subRoutine); - tryFlow.mergeSuccessors(subRoutine); - subRoutine.block.replace(catchBlock); - tryFlow.updateInOutCatch(subRoutine); + /* Now analyze and transform the subroutine. + */ + while (subRoutine.analyze(tryFlow.getNextAddr(), end)); + if (subRoutine.predecessors.size() == 1) { + /* catchFlow is synthetic, so we can safely remove it + * here. + */ + catchFlow.removeSuccessor(finallyBlock.jump); + subRoutine.mergeAddr(catchFlow); + catchFlow = subRoutine; + + if (!transformSubRoutine(subRoutine.block)) { + finallyBlock = subRoutine.block; + DescriptionBlock msg = new DescriptionBlock + ("ERROR: Missing return address handling"); + StructuredBlock subblock = subRoutine.block; + msg.replace(finallyBlock); + msg.appendBlock(finallyBlock); + } + finallyBlock = subRoutine.block; } } + + /* Now finish it. + */ + mergeFinallyBlock(tryFlow, catchFlow, finallyBlock); return true; } private boolean analyzeSpecialFinally(FlowBlock tryFlow, - StructuredBlock catchBlock, - int end) { + FlowBlock catchFlow, int end) { + StructuredBlock finallyBlock = catchFlow.block; StructuredBlock firstInstr = - catchBlock instanceof SequentialBlock - ? catchBlock.getSubBlocks()[0]: catchBlock; + finallyBlock instanceof SequentialBlock + ? finallyBlock.getSubBlocks()[0]: finallyBlock; if (!(firstInstr instanceof SpecialBlock && ((SpecialBlock)firstInstr).type == SpecialBlock.POP && ((SpecialBlock)firstInstr).count == 1)) return false; - /* This may be a special try/finally-block, where + /* This is a special try/finally-block, where * the finally block ends with a break, return or * similar. */ - FlowBlock succ = null; - /* remove the pop now */ - if (catchBlock instanceof SequentialBlock) - catchBlock = catchBlock.getSubBlocks()[1]; - else { - catchBlock = new EmptyBlock(); - catchBlock.moveJump(firstInstr.jump); + /* Make sure that resolveJump only works on the inside of the try + */ + tryFlow.lastModified = tryFlow.block.getSubBlocks()[0]; + FlowBlock finallyFlow; + if (finallyBlock instanceof SequentialBlock) { + finallyBlock = finallyBlock.getSubBlocks()[1]; + finallyFlow = null; + } else { + finallyBlock = new EmptyBlock(); + finallyBlock.moveJump(firstInstr.jump); - succ = firstInstr.jump.destination; - } - -// Set trySuccs = tryFlow.getSuccessors(); -// if (trySuccs.size() > 1 -// || (trySuccs.size() == 1 -// && trySuccs.iterator().next() != succ)) -// return false; - - if (succ != null) { - Jump jumps = tryFlow.removeJumps(succ); - /* Handle the jumps in the tryFlow. + /* Handle the jumps in the tryFlow to finallyFlow. */ - jumps = tryFlow.resolveSomeJumps(jumps, succ); - tryFlow.resolveRemaining(jumps); - } - - TryBlock tryBlock = (TryBlock)tryFlow.block; - if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { - /* remove the unnecessary tryBlock */ - TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0]; - innerTry.gen = tryBlock.gen; - innerTry.replace(tryBlock); - tryBlock = innerTry; - tryFlow.lastModified = innerTry; + finallyFlow = finallyBlock.jump.destination; + if (tryFlow.getSuccessors().contains(finallyFlow)) { + Jump jumps = tryFlow.removeJumps(finallyFlow); + jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow); + tryFlow.resolveRemaining(jumps); + } } - FinallyBlock newBlock = new FinallyBlock(); - tryBlock.addCatchBlock(newBlock); - newBlock.setCatchBlock(catchBlock); + + /* Complain about all other jumps in try block */ + Set trySuccs = tryFlow.getSuccessors(); + for (Iterator i = trySuccs.iterator(); i.hasNext(); ) { + FlowBlock succ = (FlowBlock) i.next(); + if (succ != FlowBlock.END_OF_METHOD) { + /* This seems to be a illegal succ in try block that + * doesn't go to the finally block. There is a last + * chance though. If the destination directly jumps + * to the try block, its okay. + */ + if (succ.block instanceof EmptyBlock + && succ.block.jump.destination == finallyFlow) { + Jump jumps = tryFlow.removeJumps(succ); + jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow); + tryFlow.resolveRemaining(jumps); + if (succ.predecessors.size() == 0) { + succ.removeJumps(finallyFlow); + tryFlow.mergeAddr(succ); + } + continue; + } + } + for (Jump jumps = tryFlow.getJumps(succ); + jumps != null; jumps = jumps.next) { + if (jumps.prev instanceof ThrowBlock) + continue; + + 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; } - /** - * Analyzes all exception handlers to try/catch/finally or - * synchronized blocks. - */ - public void analyze() { + 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.getAddr(); - int end = exc.endAddr; - int handler = exc.handler.getAddr(); - if (start >= end || handler < end) + Handler last = null; + for (Iterator i = handlers.iterator(); i.hasNext(); ) { + Handler exc = (Handler) i.next(); + int start = exc.start.getAddr(); + int end = exc.endAddr; + int handler = exc.handler.getAddr(); + if (start >= end || handler < end) + throw new AssertError + ("ExceptionHandler order failed: not " + + start + " < " + end + " <= " + handler); + if (last != null + && (last.start.getAddr() != start || last.endAddr != end)) { + /* The last handler does catch another range. + * Due to the order: + * start < last.start.getAddr() || end > last.end.getAddr() + */ + if (end > last.start.getAddr() && end < last.endAddr) throw new AssertError - ("ExceptionHandler order failed: not " - + start + " < " + end + " <= " + handler); - if (last != null - && (last.start.getAddr() != start || last.endAddr != end)) { - /* The last handler does catch another range. - * Due to the order: - * start < last.start.getAddr() || end > last.end.getAddr() - */ - if (end > last.start.getAddr() && end < last.endAddr) - throw new AssertError - ("Exception handlers ranges are intersecting: [" + ("Exception handlers ranges are intersecting: [" + last.start.getAddr()+", "+last.endAddr+"] and [" - + start+", "+end+"]."); - } - last = exc; + + start+", "+end+"]."); } + last = exc; } - - { - 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 endHandler = Integer.MAX_VALUE; - /* If the next exception handler catches a bigger range - * it must surround the handler completely. + } + + /** + * 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 endHandler = Integer.MAX_VALUE; + /* If the next exception handler catches a bigger range + * it must surround the handler completely. + */ + if (next != null && next.endAddr > exc.endAddr) + endHandler = next.endAddr; + + FlowBlock tryFlow = exc.start; + tryFlow.checkConsistent(); + + if (last == null || exc.type == null + || last.start.getAddr() != exc.start.getAddr() + || last.endAddr != exc.endAddr) { + /* The last handler does catch another range. + * Create a new try block. */ - if (next != null && next.endAddr > exc.endAddr) - endHandler = next.endAddr; - - FlowBlock tryFlow = exc.start; - tryFlow.checkConsistent(); - - if (last == null || exc.type == null - || last.start.getAddr() != exc.start.getAddr() - || last.endAddr != exc.endAddr) { - /* The last handler does catch another range. - * Create a new try block. - */ - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_ANALYZE) != 0) - GlobalOptions.err.println - ("analyzeTry(" - + exc.start.getAddr() + ", " + exc.endAddr+")"); - while (tryFlow.analyze(tryFlow.getAddr(), exc.endAddr)); - - TryBlock tryBlock = new TryBlock(tryFlow); - } else if (!(tryFlow.block instanceof TryBlock)) - throw new AssertError("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 ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_ANALYZE) != 0) + GlobalOptions.err.println + ("analyzeTry(" + + exc.start.getAddr() + ", " + exc.endAddr+")"); + while (tryFlow.analyze(tryFlow.getAddr(), exc.endAddr)); + + TryBlock tryBlock = new TryBlock(tryFlow); + } else if (!(tryFlow.block instanceof TryBlock)) + throw new AssertError("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. - */ - EmptyBlock jump = new EmptyBlock(new Jump(catchFlow)); - FlowBlock newFlow = new FlowBlock(catchFlow.method, - catchFlow.getAddr()); - newFlow.appendBlock(jump, 0); - catchFlow.prevByAddr.nextByAddr = newFlow; - newFlow.prevByAddr = catchFlow.prevByAddr; - newFlow.nextByAddr = catchFlow; - catchFlow.prevByAddr = newFlow; - catchFlow = newFlow; - } else { - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_ANALYZE) != 0) - GlobalOptions.err.println - ("analyzeCatch(" - + catchFlow.getAddr() + ", " + endHandler + ")"); - while (catchFlow.analyze(catchFlow.getAddr(), endHandler)); - } - - /* Merge the try-block with the catch-block now */ - tryFlow.updateInOutCatch(catchFlow); - tryFlow.mergeSuccessors(catchFlow); - tryFlow.mergeAddr(catchFlow); - if (exc.type != null) - analyzeCatchBlock(exc.type, tryFlow, catchFlow.block); - - else if (!analyzeSynchronized(tryFlow, catchFlow.block, - endHandler) - && ! analyzeFinally(tryFlow, catchFlow.block, - endHandler) - && ! analyzeSpecialFinally(tryFlow, catchFlow.block, - endHandler)) - - analyzeCatchBlock(Type.tObject, tryFlow, catchFlow.block); - - tryFlow.checkConsistent(); + } + + 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. + */ + EmptyBlock jump = new EmptyBlock(new Jump(catchFlow)); + FlowBlock newFlow = new FlowBlock(catchFlow.method, + catchFlow.getAddr()); + newFlow.appendBlock(jump, 0); + catchFlow.prevByAddr.nextByAddr = newFlow; + newFlow.prevByAddr = catchFlow.prevByAddr; + newFlow.nextByAddr = catchFlow; + catchFlow.prevByAddr = newFlow; + catchFlow = newFlow; + } else { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_ANALYZE) != 0) GlobalOptions.err.println - ("analyzeTryCatch(" + tryFlow.getAddr() + ", " - + tryFlow.getNextAddr() + ") done."); + ("analyzeCatch(" + + catchFlow.getAddr() + ", " + endHandler + ")"); + while (catchFlow.analyze(catchFlow.getAddr(), endHandler)); } + + if (exc.type != null) + analyzeCatchBlock(exc.type, tryFlow, catchFlow); + + else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler) + && ! analyzeFinally(tryFlow, catchFlow, endHandler) + && ! analyzeSpecialFinally(tryFlow, catchFlow, + endHandler)) + + analyzeCatchBlock(Type.tObject, tryFlow, catchFlow); + + tryFlow.checkConsistent(); + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_ANALYZE) != 0) + GlobalOptions.err.println + ("analyzeTryCatch(" + tryFlow.getAddr() + ", " + + tryFlow.getNextAddr() + ") done."); } } }