Fixed Java-1.1 and finally/synchronized blocks.

git-svn-id: https://svn.code.sf.net/p/jode/code/branches/branch_1_1@1343 379699f6-c40d-0410-875b-85095c16579e
branch_1_1
hoenicke 24 years ago
parent c79f6122f5
commit 03497ae1a2
  1. 81
      jode/ChangeLog
  2. 5
      jode/jode/flow/FlowBlock.java.in
  3. 619
      jode/jode/flow/TransformExceptionHandlers.java.in

@ -1,3 +1,35 @@
2001-08-12 Jochen Hoenicke <jochen@gnu.org>
* 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 <jochen@gnu.org> 2001-08-10 Jochen Hoenicke <jochen@gnu.org>
* configure.in: Changed bash syntax to more compatible (but * configure.in: Changed bash syntax to more compatible (but
@ -23,7 +55,7 @@
* jode/obfuscator/modules/SimpleAnalyzer.java.in: * jode/obfuscator/modules/SimpleAnalyzer.java.in:
Ported fix from ConstantAnalyzer: Ported fix from ConstantAnalyzer:
(canonizeReference): for interfaces call canonizeIfaceReference. (canonizeReference): for interfaces call canonizeIfaceReference.
(canonizeIfaceReference): new method. (canonizeIfaceReference): new method.
* jode/obfuscator/modules/ConstantAnalyzer.java.in: * jode/obfuscator/modules/ConstantAnalyzer.java.in:
made sub class of SimpleAnalyzer. made sub class of SimpleAnalyzer.
@ -32,8 +64,7 @@
2001-07-08 Jochen Hoenicke <jochen@gnu.org> 2001-07-08 Jochen Hoenicke <jochen@gnu.org>
* jode/bytecode/BytecodeInfo.java.in (calculateMaxStack): Handle * jode/bytecode/BytecodeInfo.java.in (calculateMaxStack): Handle special case for empty method. Previous code would just crash.
special case for empty method. Previous code would just crash.
2001-06-15 Jochen Hoenicke <jochen@gnu.org> 2001-06-15 Jochen Hoenicke <jochen@gnu.org>
@ -166,8 +197,8 @@
2001-05-08 Jochen Hoenicke <jochen@gnu.org> 2001-05-08 Jochen Hoenicke <jochen@gnu.org>
* jode/jvm/CodeVerifier.java.in (doVerify): Don't check for * jode/jvm/CodeVerifier.java.in (doVerify): Don't check for
uninitialized objects in local or stack slots on backwards jump or uninitialized objects in local or stack slots on backwards jump or
exception blocks. Sun's jdk also doesn't check it, and I never 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. understood why it is necessary. But see JVM Spec 4.9.4.
2001-05-02 Jochen Hoenicke <jochen@gnu.org> 2001-05-02 Jochen Hoenicke <jochen@gnu.org>
@ -225,7 +256,7 @@
2001-02-08 Jochen Hoenicke <jochen@gnu.org> 2001-02-08 Jochen Hoenicke <jochen@gnu.org>
* jode/expr/StoreInstruction.java (dumpExpression): Java doesn't * 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. call lhs.dumpExpression() with priority.
* jode/expr/PrePostFixOperator.java (dumpExpression): likewise. * jode/expr/PrePostFixOperator.java (dumpExpression): likewise.
* jode/expr/IIncOperator.java (dumpExpression): likewise. * jode/expr/IIncOperator.java (dumpExpression): likewise.
@ -243,7 +274,7 @@
ref.getClazz() gives a type signature. ref.getClazz() gives a type signature.
* jode/flow/TransformExceptionHandlers.java.in: * jode/flow/TransformExceptionHandlers.java.in:
(checkAndRemoveJSR): Only invoke removeBadJSR, if there are (checkAndRemoveJSR): Only invoke removeBadJSR, if there are
successors left. successors left.
(checkAndRemoveMonitorExit): likewise. (checkAndRemoveMonitorExit): likewise.
2001-02-04 Jochen Hoenicke <jochen@gnu.org> 2001-02-04 Jochen Hoenicke <jochen@gnu.org>
@ -321,14 +352,14 @@
* jode/decompiler/Options.java (GNU_SPACING): new constant. * jode/decompiler/Options.java (GNU_SPACING): new constant.
(GNU_STYLE): changed to include GNU_SPACING. (GNU_STYLE): changed to include GNU_SPACING.
* jode/decompiler/ClassAnalyzer.java.in (dumpSource): Use * jode/decompiler/ClassAnalyzer.java.in (dumpSource): Use
open/closeBraceClass. open/closeBraceClass.
* jode/decompiler/MethodAnalyzer.java.in (dumpSource): Use * 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 * jode/decompiler/InvokeOperator.java.in (dumpExpression): Insert
a space for GNU_SPACING, use open/closeBraceClass for inner a space for GNU_SPACING, use open/closeBraceClass for inner
classes. classes.
* jode/decompiler/UnaryOperator.java (dumpExpression): Insert * jode/decompiler/UnaryOperator.java (dumpExpression): Insert
a space for GNU_SPACING. a space for GNU_SPACING.
2001-01-30 Jochen Hoenicke <jochen@gnu.org> 2001-01-30 Jochen Hoenicke <jochen@gnu.org>
@ -338,30 +369,30 @@
* jode/decompiler/Decompiler.java (setOption): detect pascal option. * jode/decompiler/Decompiler.java (setOption): detect pascal option.
* jode/decompiler/Main.java (main): dito. * jode/decompiler/Main.java (main): dito.
* jode/decompiler/TabbedPrintWriter.java (openBrace, * jode/decompiler/TabbedPrintWriter.java (openBrace,
openBraceContinue, closeBrace, closeBraceNoSpace, openBraceContinue, closeBrace, closeBraceNoSpace,
closeBraceContinue): handle flush left. closeBraceContinue): handle flush left.
2001-01-30 Jochen Hoenicke <jochen@gnu.org> 2001-01-30 Jochen Hoenicke <jochen@gnu.org>
* jode/type/NullType.java (intersection): Removed, since the * jode/type/NullType.java (intersection): Removed, since the
version in ReferenceType is more correct. Before version in ReferenceType is more correct. Before
tNull.isOfType(tRange(X,tNull)) returned false, which lead to tNull.isOfType(tRange(X,tNull)) returned false, which lead to
incorrect behaviour in InvokeOperator.needsCast. incorrect behaviour in InvokeOperator.needsCast.
* jode/decompiler/FieldAnalyzer.java.in (dumpSource): Removed the * jode/decompiler/FieldAnalyzer.java.in (dumpSource): Removed the
"= null" hack for final fields; it was not correct, since the "= null" hack for final fields; it was not correct, since the
field could be initialized in a constructor. field could be initialized in a constructor.
* jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp): * jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp):
Simplified the code, copy options always from child. Simplified the code, copy options always from child.
* jode/jvm/SyntheticAnalyzer.java.in (unifyParam): new field. * jode/jvm/SyntheticAnalyzer.java.in (unifyParam): new field.
(checkConstructorAccess): Allow the special Parameter, whose (checkConstructorAccess): Allow the special Parameter, whose
purpose is to distinguish the wrapper from the real constructor purpose is to distinguish the wrapper from the real constructor
and give him a "$" in the type signature, to appear at every and give him a "$" in the type signature, to appear at every
position. It doesn't appear at position 1 for inner classes. position. It doesn't appear at position 1 for inner classes.
Store the position in unifyParam. Store the position in unifyParam.
* jode/expr/InvokeOperator.java (isGetClass): Allow the method to * 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. the method analyzer.
(simplify): handle unifyParam. (simplify): handle unifyParam.
* jode/expr/PopOperator.java (getBreakPenalty): return penalty of * jode/expr/PopOperator.java (getBreakPenalty): return penalty of
inner expression. (dumpExpression): Call dumpExpression of inner expression. (dumpExpression): Call dumpExpression of
subexpression immediately without priority. subexpression immediately without priority.

@ -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 * 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 * it under the terms of the GNU General Public License as published by
@ -748,7 +748,8 @@ public class FlowBlock {
StructuredBlock last = lastModified; StructuredBlock last = lastModified;
while (last.outer instanceof SequentialBlock while (last.outer instanceof SequentialBlock
|| last.outer instanceof TryBlock) || last.outer instanceof TryBlock
|| last.outer instanceof FinallyBlock)
last = last.outer; last = last.outer;
if (last.outer != null) if (last.outer != null)
throw new AssertError("Inconsistency"); throw new AssertError("Inconsistency");

@ -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). <br>
*
* This method doesn't actually merge the contents of the blocks. The
* caller should do it right afterwards. <br>
* *
* try-header * The flow block catchFlow mustn't have any predecessors.
* |- first instruction * @param tryFlow the flow block containing the try.
* | ... * @param catchFlow the flow block containing the catch handler.
* | last instruction */
* |- optional jump (last+1) static void mergeTryCatch(FlowBlock tryFlow, FlowBlock catchFlow) {
* | ... if ((GlobalOptions.debuggingFlags
* `- catch block & 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. <br>
* 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, 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); CatchBlock newBlock = new CatchBlock(type);
((TryBlock)tryFlow.block).addCatchBlock(newBlock); ((TryBlock)tryFlow.block).addCatchBlock(newBlock);
newBlock.setCatchBlock(catchBlock); newBlock.setCatchBlock(catchFlow.block);
} }
/* And now the complicated parts. */ /* And now the complicated parts. */
/** /**
* This transforms a sub routine, that is checks if the beginning * This transforms a sub routine, i.e. it checks if the beginning
* local assignment matches the final ret and then returns. * local assignment matches the final ret and removes both. It also
* accepts sub routines that just pop their return address.
*/ */
boolean transformSubRoutine(StructuredBlock subRoutine) { boolean transformSubRoutine(StructuredBlock subRoutineBlock) {
if (!(subRoutine instanceof SequentialBlock)) StructuredBlock firstBlock = subRoutineBlock;
return false; if (firstBlock instanceof SequentialBlock)
SequentialBlock sequBlock = (SequentialBlock) subRoutine; firstBlock = subRoutineBlock.getSubBlocks()[0];
StructuredBlock firstBlock = sequBlock.getSubBlocks()[0];
LocalInfo local = null; LocalInfo local = null;
if (firstBlock instanceof SpecialBlock) { if (firstBlock instanceof SpecialBlock) {
@ -152,24 +180,25 @@ public class TransformExceptionHandlers {
} else } else
return false; return false;
/* We have now committed. Remove the first Statement which /* We are now committed and can start changing code. Remove
* stores/removes the return address. * the first Statement which stores/removes the return
* address.
*/ */
firstBlock.removeBlock(); 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 * This is a complicated task which isn't needed for javac nor
* jikes. We just check if the last instruction is a ret and * 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. * as long as the bytecode was verified correctly.
*/ */
while (sequBlock.subBlocks[1] instanceof SequentialBlock) while (subRoutineBlock instanceof SequentialBlock)
sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; subRoutineBlock = subRoutineBlock.getSubBlocks()[1];
if (sequBlock.subBlocks[1] instanceof RetBlock if (subRoutineBlock instanceof RetBlock
&& (((RetBlock) sequBlock.subBlocks[1]).local.equals(local))) { && (((RetBlock) subRoutineBlock).local.equals(local))) {
sequBlock.subBlocks[1].removeBlock(); subRoutineBlock.removeBlock();
} }
return true; return true;
} }
@ -199,51 +228,32 @@ public class TransformExceptionHandlers {
} }
/** /**
* Remove the wrongly placed JSRs jumping to the specified * Remove the JSRs jumping to the specified subRoutine. The right
* subRoutine. The right JSRs are already removed, but we have to * JSRs are marked and we can just remove them. For the other JSR
* replace the wrong ones with a warning. * instructions we replace them with a warning.
* @param tryFlow the FlowBLock of the try block. * @param tryFlow the FlowBlock of the try block.
* @param subRoutine the FlowBlock of the sub routine. * @param subRoutine the FlowBlock of the sub routine.
*/ */
private void removeJSR(FlowBlock tryFlow, StructuredBlock catchBlock, private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) {
FlowBlock subRoutine) { for (Jump jumps = tryFlow.removeJumps(subRoutine);
Jump nextJump; jumps != null; jumps = jumps.next) {
for (Jump jumps = tryFlow.getJumps(subRoutine);
jumps != null; jumps = nextJump) {
StructuredBlock prev = jumps.prev; StructuredBlock prev = jumps.prev;
nextJump = jumps.next; prev.removeJump();
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;
}
tryFlow.removeSuccessor(jumps); if (prev instanceof EmptyBlock
prev.removeJump(); && prev.outer instanceof JsrBlock
if (jsr.isGood()) { && ((JsrBlock) prev.outer).isGood()) {
StructuredBlock next = jsr.getNextBlock(); StructuredBlock next = prev.outer.getNextBlock();
jsr.removeBlock(); prev.outer.removeBlock();
if (next instanceof ReturnBlock) if (next instanceof ReturnBlock)
removeReturnLocal((ReturnBlock) next); removeReturnLocal((ReturnBlock) next);
} else { } else {
/* We have a JSR to the subroutine, which is badly placed. /* We have a jump to the subroutine, that 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.
* We complain here. * We complain here.
*/ */
DescriptionBlock msg DescriptionBlock msg = new DescriptionBlock
= new DescriptionBlock("ERROR: GOTO FINALLY BLOCK!"); ("ERROR: invalid jump to finally block!");
tryFlow.removeSuccessor(jumps);
prev.removeJump();
prev.appendBlock(msg); prev.appendBlock(msg);
} }
} }
@ -309,7 +319,6 @@ public class TransformExceptionHandlers {
private void checkAndRemoveJSR(FlowBlock tryFlow, private void checkAndRemoveJSR(FlowBlock tryFlow,
StructuredBlock catchBlock,
FlowBlock subRoutine, FlowBlock subRoutine,
int startOutExit, int endOutExit) { int startOutExit, int endOutExit) {
Iterator iter = tryFlow.getSuccessors().iterator(); Iterator iter = tryFlow.getSuccessors().iterator();
@ -332,8 +341,8 @@ public class TransformExceptionHandlers {
} }
if (prev instanceof EmptyBlock if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) { && prev.outer instanceof JsrBlock) {
/* This jump is really a jsr, since it doesn't /* This jump is a jsr, since it doesn't leave the
* leave the block forever, we can ignore it. * block forever, we can ignore it.
*/ */
continue; continue;
} }
@ -384,7 +393,7 @@ public class TransformExceptionHandlers {
* Complain! * Complain!
*/ */
DescriptionBlock msg DescriptionBlock msg
= new DescriptionBlock("ERROR: NO JSR TO FINALLY"); = new DescriptionBlock("ERROR: no jsr to finally");
if (pred != null) if (pred != null)
pred.prependBlock(msg); pred.prependBlock(msg);
else { else {
@ -394,11 +403,10 @@ public class TransformExceptionHandlers {
} }
} }
if (tryFlow.getSuccessors().contains(subRoutine)) if (tryFlow.getSuccessors().contains(subRoutine))
removeJSR(tryFlow, catchBlock, subRoutine); removeJSR(tryFlow, subRoutine);
} }
private void checkAndRemoveMonitorExit(FlowBlock tryFlow, private void checkAndRemoveMonitorExit(FlowBlock tryFlow,
StructuredBlock catchBlock,
LocalInfo local, LocalInfo local,
int start, int end) { int start, int end) {
FlowBlock subRoutine = null; FlowBlock subRoutine = null;
@ -502,7 +510,7 @@ public class TransformExceptionHandlers {
/* Complain! /* Complain!
*/ */
DescriptionBlock msg DescriptionBlock msg
= new DescriptionBlock("ERROR: NO MONITOREXIT"); = new DescriptionBlock("ERROR: no monitorexit");
prev.appendBlock(msg); prev.appendBlock(msg);
msg.moveJump(jumps); msg.moveJump(jumps);
} }
@ -510,8 +518,9 @@ public class TransformExceptionHandlers {
if (subRoutine != null) { if (subRoutine != null) {
if (tryFlow.getSuccessors().contains(subRoutine)) if (tryFlow.getSuccessors().contains(subRoutine))
removeJSR(tryFlow, catchBlock, subRoutine); removeJSR(tryFlow, subRoutine);
tryFlow.mergeAddr(subRoutine); if (subRoutine.predecessors.size() == 0)
tryFlow.mergeAddr(subRoutine);
} }
} }
@ -534,20 +543,25 @@ public class TransformExceptionHandlers {
} }
private boolean analyzeSynchronized(FlowBlock tryFlow, private boolean analyzeSynchronized(FlowBlock tryFlow,
StructuredBlock catchBlock, FlowBlock catchFlow,
int endHandler) { 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); StoreInstruction excStore = getExceptionStore(catchBlock);
if (excStore != null) if (excStore != null)
catchBlock = catchBlock.getSubBlocks()[1]; catchBlock = catchBlock.getSubBlocks()[1];
/* Check for the monitorexit instruction */
if (!(catchBlock instanceof SequentialBlock if (!(catchBlock instanceof SequentialBlock
&& catchBlock.getSubBlocks()[0] && catchBlock.getSubBlocks()[0]
instanceof InstructionBlock)) instanceof InstructionBlock))
return false; return false;
Expression instr = Expression instr =
((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction(); ((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction();
if (!(instr instanceof MonitorExitOperator if (!(instr instanceof MonitorExitOperator
&& instr.getFreeOperandCount() == 0 && instr.getFreeOperandCount() == 0
&& (((MonitorExitOperator)instr).getSubExpressions()[0] && (((MonitorExitOperator)instr).getSubExpressions()[0]
@ -592,6 +606,16 @@ public class TransformExceptionHandlers {
* return_n * 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) MonitorExitOperator monexit = (MonitorExitOperator)
((InstructionBlock) catchBlock.getSubBlocks()[0]).instr; ((InstructionBlock) catchBlock.getSubBlocks()[0]).instr;
LocalInfo local = LocalInfo local =
@ -605,7 +629,7 @@ public class TransformExceptionHandlers {
+ "," + tryFlow.getNextAddr() + "," + endHandler + ")"); + "," + tryFlow.getNextAddr() + "," + endHandler + ")");
checkAndRemoveMonitorExit checkAndRemoveMonitorExit
(tryFlow, catchBlock, local, tryFlow.getNextAddr(), endHandler); (tryFlow, local, tryFlow.getNextAddr(), endHandler);
SynchronizedBlock syncBlock = new SynchronizedBlock(local); SynchronizedBlock syncBlock = new SynchronizedBlock(local);
TryBlock tryBlock = (TryBlock) tryFlow.block; TryBlock tryBlock = (TryBlock) tryFlow.block;
@ -617,8 +641,41 @@ public class TransformExceptionHandlers {
return true; 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, private boolean analyzeFinally(FlowBlock tryFlow,
StructuredBlock catchBlock, int end) { FlowBlock catchFlow, int end) {
/* Layout of a try-finally block: /* Layout of a try-finally block:
* *
@ -640,6 +697,7 @@ public class TransformExceptionHandlers {
* return_n * return_n
*/ */
StructuredBlock catchBlock = catchFlow.block;
StoreInstruction excStore = getExceptionStore(catchBlock); StoreInstruction excStore = getExceptionStore(catchBlock);
if (excStore == null) if (excStore == null)
return false; return false;
@ -651,9 +709,10 @@ public class TransformExceptionHandlers {
StructuredBlock finallyBlock = null; StructuredBlock finallyBlock = null;
if (catchBlock.getSubBlocks()[0] instanceof LoopBlock) { if (catchBlock.getSubBlocks()[0] instanceof LoopBlock) {
/* In case the try block has no exit (that means, it throws /* In case the try block has no exit (that means, it
* an exception), the finallyBlock was already merged with * throws an exception or loops forever), the finallyBlock
* the catchBlock. We have to check for this case separately: * was already merged with the catchBlock. We have to
* check for this case separately:
* *
* do { * do {
* JSR * JSR
@ -690,262 +749,270 @@ public class TransformExceptionHandlers {
FlowBlock subRoutine; FlowBlock subRoutine;
if (finallyBlock != null) { 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 * need to check if it breaks to the right block, because
* we know that there is only one Block around the jsr. * we know that there is only one Block around the jsr.
*/ */
if (!(jsrBlock.innerBlock instanceof BreakBlock)) if (!(jsrBlock.innerBlock instanceof BreakBlock))
return false; return false;
/* Check if the try block has no exit (except throws) /* TODO - Check if the try block has no exit (except throws)
* XXX - Unfortunately the try block already has the
* successors of catch block.
*/ */
// if (tryFlow.getSuccessors() > 0) // if (tryFlow.getSuccessors().size() > 0)
// return false; // return false;
catchBlock = finallyBlock; catchBlock = finallyBlock;
subRoutine = null; subRoutine = null;
catchFlow.removeSuccessor(throwBlock.jump);
} else { } else {
if (!(jsrBlock.innerBlock instanceof EmptyBlock)) if (!(jsrBlock.innerBlock instanceof EmptyBlock))
return false; return false;
catchBlock = jsrBlock; finallyBlock = jsrBlock.innerBlock;
subRoutine = jsrBlock.innerBlock.jump.destination; subRoutine = finallyBlock.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; /* We are committed now and can start changing the blocks.
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);
if (subRoutine != null) {
while (subRoutine.analyze(tryFlow.getNextAddr(), end));
/* Now check if the subroutine is correct and has only the
* catchFlow as predecessor.
*/ */
if (subRoutine.predecessors.size() == 1 catchFlow.removeSuccessor(throwBlock.jump);
&& transformSubRoutine(subRoutine.block)) { checkAndRemoveJSR(tryFlow, subRoutine,
tryFlow.getNextAddr(), end);
tryFlow.removeSuccessor(jsrBlock.innerBlock.jump); /* Now analyze and transform the subroutine.
tryFlow.mergeAddr(subRoutine); */
tryFlow.mergeSuccessors(subRoutine); while (subRoutine.analyze(tryFlow.getNextAddr(), end));
subRoutine.block.replace(catchBlock); if (subRoutine.predecessors.size() == 1) {
tryFlow.updateInOutCatch(subRoutine); /* 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; return true;
} }
private boolean analyzeSpecialFinally(FlowBlock tryFlow, private boolean analyzeSpecialFinally(FlowBlock tryFlow,
StructuredBlock catchBlock, FlowBlock catchFlow, int end) {
int end) { StructuredBlock finallyBlock = catchFlow.block;
StructuredBlock firstInstr = StructuredBlock firstInstr =
catchBlock instanceof SequentialBlock finallyBlock instanceof SequentialBlock
? catchBlock.getSubBlocks()[0]: catchBlock; ? finallyBlock.getSubBlocks()[0]: finallyBlock;
if (!(firstInstr instanceof SpecialBlock if (!(firstInstr instanceof SpecialBlock
&& ((SpecialBlock)firstInstr).type == SpecialBlock.POP && ((SpecialBlock)firstInstr).type == SpecialBlock.POP
&& ((SpecialBlock)firstInstr).count == 1)) && ((SpecialBlock)firstInstr).count == 1))
return false; 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 * the finally block ends with a break, return or
* similar. * similar.
*/ */
FlowBlock succ = null;
/* remove the pop now */ /* Make sure that resolveJump only works on the inside of the try
if (catchBlock instanceof SequentialBlock) */
catchBlock = catchBlock.getSubBlocks()[1]; tryFlow.lastModified = tryFlow.block.getSubBlocks()[0];
else { FlowBlock finallyFlow;
catchBlock = new EmptyBlock(); if (finallyBlock instanceof SequentialBlock) {
catchBlock.moveJump(firstInstr.jump); finallyBlock = finallyBlock.getSubBlocks()[1];
finallyFlow = null;
} else {
finallyBlock = new EmptyBlock();
finallyBlock.moveJump(firstInstr.jump);
succ = firstInstr.jump.destination; /* Handle the jumps in the tryFlow to finallyFlow.
*/
finallyFlow = finallyBlock.jump.destination;
if (tryFlow.getSuccessors().contains(finallyFlow)) {
Jump jumps = tryFlow.removeJumps(finallyFlow);
jumps = tryFlow.resolveSomeJumps(jumps, finallyFlow);
tryFlow.resolveRemaining(jumps);
}
} }
// Set trySuccs = tryFlow.getSuccessors(); /* Complain about all other jumps in try block */
// if (trySuccs.size() > 1 Set trySuccs = tryFlow.getSuccessors();
// || (trySuccs.size() == 1 for (Iterator i = trySuccs.iterator(); i.hasNext(); ) {
// && trySuccs.iterator().next() != succ)) FlowBlock succ = (FlowBlock) i.next();
// return false; 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;
if (succ != null) { DescriptionBlock msg =
Jump jumps = tryFlow.removeJumps(succ); new DescriptionBlock
/* Handle the jumps in the tryFlow. ("ERROR: doesn't go through finally block!");
*/ if (jumps.prev instanceof ReturnBlock) {
jumps = tryFlow.resolveSomeJumps(jumps, succ); msg.replace(jumps.prev);
tryFlow.resolveRemaining(jumps); msg.appendBlock(jumps.prev);
} } else {
jumps.prev.appendBlock(msg);
msg.moveJump(jumps);
}
}
}
TryBlock tryBlock = (TryBlock)tryFlow.block; mergeFinallyBlock(tryFlow, catchFlow, finallyBlock);
if (tryBlock.getSubBlocks()[0] instanceof TryBlock) { /* Following code will work be put inside the finallyBlock */
/* remove the unnecessary tryBlock */ tryFlow.lastModified = finallyBlock;
TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0];
innerTry.gen = tryBlock.gen;
innerTry.replace(tryBlock);
tryBlock = innerTry;
tryFlow.lastModified = innerTry;
}
FinallyBlock newBlock = new FinallyBlock();
tryBlock.addCatchBlock(newBlock);
newBlock.setCatchBlock(catchBlock);
return true; return true;
} }
/** void checkTryCatchOrder() {
* Analyzes all exception handlers to try/catch/finally or
* synchronized blocks.
*/
public void analyze() {
/* Check if try/catch ranges are okay. The following succeeds /* Check if try/catch ranges are okay. The following succeeds
* for all classes generated by the sun java compiler, but hand * for all classes generated by the sun java compiler, but hand
* optimized classes (or generated by other compilers) will fail. * optimized classes (or generated by other compilers) will fail.
*/ */
{ Handler last = null;
Handler last = null; for (Iterator i = handlers.iterator(); i.hasNext(); ) {
for (Iterator i = handlers.iterator(); i.hasNext(); ) { Handler exc = (Handler) i.next();
Handler exc = (Handler) i.next(); int start = exc.start.getAddr();
int start = exc.start.getAddr(); int end = exc.endAddr;
int end = exc.endAddr; int handler = exc.handler.getAddr();
int handler = exc.handler.getAddr(); if (start >= end || handler < end)
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 throw new AssertError
("ExceptionHandler order failed: not " ("Exception handlers ranges are intersecting: ["
+ 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: ["
+ last.start.getAddr()+", "+last.endAddr+"] and [" + last.start.getAddr()+", "+last.endAddr+"] and ["
+ start+", "+end+"]."); + start+", "+end+"].");
}
last = exc;
} }
last = exc;
} }
}
{ /**
Iterator i = handlers.iterator(); * Analyzes all exception handlers to try/catch/finally or
Handler exc = null; * synchronized blocks.
Handler next = i.hasNext() ? (Handler) i.next() : null; */
while(next != null) { public void analyze() {
Handler last = exc; checkTryCatchOrder();
exc = next;
next = i.hasNext() ? (Handler) i.next() : null; Iterator i = handlers.iterator();
int endHandler = Integer.MAX_VALUE; Handler exc = null;
/* If the next exception handler catches a bigger range Handler next = i.hasNext() ? (Handler) i.next() : null;
* it must surround the handler completely. while(next != null) {
*/ Handler last = exc;
if (next != null && next.endAddr > exc.endAddr) exc = next;
endHandler = next.endAddr; 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; FlowBlock tryFlow = exc.start;
tryFlow.checkConsistent(); tryFlow.checkConsistent();
if (last == null || exc.type == null if (last == null || exc.type == null
|| last.start.getAddr() != exc.start.getAddr() || last.start.getAddr() != exc.start.getAddr()
|| last.endAddr != exc.endAddr) { || last.endAddr != exc.endAddr) {
/* The last handler does catch another range. /* The last handler does catch another range.
* Create a new try block. * Create a new try block.
*/ */
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeTry(" ("analyzeTry("
+ exc.start.getAddr() + ", " + exc.endAddr+")"); + exc.start.getAddr() + ", " + exc.endAddr+")");
while (tryFlow.analyze(tryFlow.getAddr(), exc.endAddr)); while (tryFlow.analyze(tryFlow.getAddr(), exc.endAddr));
TryBlock tryBlock = new TryBlock(tryFlow); TryBlock tryBlock = new TryBlock(tryFlow);
} else if (!(tryFlow.block instanceof TryBlock)) } else if (!(tryFlow.block instanceof TryBlock))
throw new AssertError("no TryBlock"); throw new AssertError("no TryBlock");
FlowBlock catchFlow = exc.handler; FlowBlock catchFlow = exc.handler;
boolean isMultiUsed = catchFlow.predecessors.size() != 0; boolean isMultiUsed = catchFlow.predecessors.size() != 0;
if (!isMultiUsed && next != null) { if (!isMultiUsed && next != null) {
for (Iterator j = handlers.tailSet(next).iterator(); for (Iterator j = handlers.tailSet(next).iterator();
j.hasNext();) { j.hasNext();) {
Handler h = (Handler) j.next(); Handler h = (Handler) j.next();
if (h.handler == catchFlow) { if (h.handler == catchFlow) {
isMultiUsed = true; isMultiUsed = true;
break; break;
}
} }
} }
}
if (isMultiUsed) { if (isMultiUsed) {
/* If this exception is used in other exception handlers, /* If this exception is used in other exception handlers,
* create a new flow block, that jumps to the handler. * create a new flow block, that jumps to the handler.
* This will be our new exception handler. * This will be our new exception handler.
*/ */
EmptyBlock jump = new EmptyBlock(new Jump(catchFlow)); EmptyBlock jump = new EmptyBlock(new Jump(catchFlow));
FlowBlock newFlow = new FlowBlock(catchFlow.method, FlowBlock newFlow = new FlowBlock(catchFlow.method,
catchFlow.getAddr()); catchFlow.getAddr());
newFlow.appendBlock(jump, 0); newFlow.appendBlock(jump, 0);
catchFlow.prevByAddr.nextByAddr = newFlow; catchFlow.prevByAddr.nextByAddr = newFlow;
newFlow.prevByAddr = catchFlow.prevByAddr; newFlow.prevByAddr = catchFlow.prevByAddr;
newFlow.nextByAddr = catchFlow; newFlow.nextByAddr = catchFlow;
catchFlow.prevByAddr = newFlow; catchFlow.prevByAddr = newFlow;
catchFlow = newFlow; catchFlow = newFlow;
} else { } else {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeCatch(" ("analyzeCatch("
+ catchFlow.getAddr() + ", " + endHandler + ")"); + catchFlow.getAddr() + ", " + endHandler + ")");
while (catchFlow.analyze(catchFlow.getAddr(), endHandler)); while (catchFlow.analyze(catchFlow.getAddr(), endHandler));
} }
/* Merge the try-block with the catch-block now */ if (exc.type != null)
tryFlow.updateInOutCatch(catchFlow); analyzeCatchBlock(exc.type, tryFlow, catchFlow);
tryFlow.mergeSuccessors(catchFlow);
tryFlow.mergeAddr(catchFlow);
if (exc.type != null)
analyzeCatchBlock(exc.type, tryFlow, catchFlow.block);
else if (!analyzeSynchronized(tryFlow, catchFlow.block, else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler)
endHandler) && ! analyzeFinally(tryFlow, catchFlow, endHandler)
&& ! analyzeFinally(tryFlow, catchFlow.block, && ! analyzeSpecialFinally(tryFlow, catchFlow,
endHandler) endHandler))
&& ! analyzeSpecialFinally(tryFlow, catchFlow.block,
endHandler))
analyzeCatchBlock(Type.tObject, tryFlow, catchFlow.block); analyzeCatchBlock(Type.tObject, tryFlow, catchFlow);
tryFlow.checkConsistent(); tryFlow.checkConsistent();
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_ANALYZE) != 0) & GlobalOptions.DEBUG_ANALYZE) != 0)
GlobalOptions.err.println GlobalOptions.err.println
("analyzeTryCatch(" + tryFlow.getAddr() + ", " ("analyzeTryCatch(" + tryFlow.getAddr() + ", "
+ tryFlow.getNextAddr() + ") done."); + tryFlow.getNextAddr() + ") done.");
}
} }
} }
} }

Loading…
Cancel
Save