|
|
|
@ -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 |
|
|
|
|
* |- first instruction |
|
|
|
|
* | ... |
|
|
|
|
* | last instruction |
|
|
|
|
* |- optional jump (last+1) |
|
|
|
|
* | ... |
|
|
|
|
* `- catch block |
|
|
|
|
* 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. <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, |
|
|
|
|
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] |
|
|
|
@ -592,6 +606,16 @@ public class TransformExceptionHandlers { |
|
|
|
|
* 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; |
|
|
|
|
LocalInfo local = |
|
|
|
@ -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 |
|
|
|
|
*/ |
|
|
|
|
finallyBlock = jsrBlock.innerBlock; |
|
|
|
|
subRoutine = finallyBlock.jump.destination; |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
/* 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();
|
|
|
|
|
// if (trySuccs.size() > 1
|
|
|
|
|
// || (trySuccs.size() == 1
|
|
|
|
|
// && trySuccs.iterator().next() != succ))
|
|
|
|
|
// return false;
|
|
|
|
|
/* 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; |
|
|
|
|
|
|
|
|
|
if (succ != null) { |
|
|
|
|
Jump jumps = tryFlow.removeJumps(succ); |
|
|
|
|
/* Handle the jumps in the tryFlow. |
|
|
|
|
*/ |
|
|
|
|
jumps = tryFlow.resolveSomeJumps(jumps, succ); |
|
|
|
|
tryFlow.resolveRemaining(jumps); |
|
|
|
|
} |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
FinallyBlock newBlock = new FinallyBlock(); |
|
|
|
|
tryBlock.addCatchBlock(newBlock); |
|
|
|
|
newBlock.setCatchBlock(catchBlock); |
|
|
|
|
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. |
|
|
|
|
*/ |
|
|
|
|
if (next != null && next.endAddr > exc.endAddr) |
|
|
|
|
endHandler = next.endAddr; |
|
|
|
|
/** |
|
|
|
|
* 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(); |
|
|
|
|
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 (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 (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(" |
|
|
|
|
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)); |
|
|
|
|
} |
|
|
|
|
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); |
|
|
|
|
if (exc.type != null) |
|
|
|
|
analyzeCatchBlock(exc.type, tryFlow, catchFlow); |
|
|
|
|
|
|
|
|
|
else if (!analyzeSynchronized(tryFlow, catchFlow.block, |
|
|
|
|
endHandler) |
|
|
|
|
&& ! analyzeFinally(tryFlow, catchFlow.block, |
|
|
|
|
endHandler) |
|
|
|
|
&& ! analyzeSpecialFinally(tryFlow, catchFlow.block, |
|
|
|
|
endHandler)) |
|
|
|
|
else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler) |
|
|
|
|
&& ! analyzeFinally(tryFlow, catchFlow, endHandler) |
|
|
|
|
&& ! analyzeSpecialFinally(tryFlow, catchFlow, |
|
|
|
|
endHandler)) |
|
|
|
|
|
|
|
|
|
analyzeCatchBlock(Type.tObject, tryFlow, catchFlow.block); |
|
|
|
|
analyzeCatchBlock(Type.tObject, tryFlow, catchFlow); |
|
|
|
|
|
|
|
|
|
tryFlow.checkConsistent(); |
|
|
|
|
if ((GlobalOptions.debuggingFlags |
|
|
|
|
& GlobalOptions.DEBUG_ANALYZE) != 0) |
|
|
|
|
GlobalOptions.err.println |
|
|
|
|
("analyzeTryCatch(" + tryFlow.getAddr() + ", " |
|
|
|
|
+ tryFlow.getNextAddr() + ") done."); |
|
|
|
|
} |
|
|
|
|
tryFlow.checkConsistent(); |
|
|
|
|
if ((GlobalOptions.debuggingFlags |
|
|
|
|
& GlobalOptions.DEBUG_ANALYZE) != 0) |
|
|
|
|
GlobalOptions.err.println |
|
|
|
|
("analyzeTryCatch(" + tryFlow.getAddr() + ", " |
|
|
|
|
+ tryFlow.getNextAddr() + ") done."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|