|
|
|
@ -109,16 +109,12 @@ public class TransformExceptionHandlers { |
|
|
|
|
* | ... |
|
|
|
|
* `- catch block |
|
|
|
|
*/ |
|
|
|
|
static void analyzeCatchBlock(Type type, |
|
|
|
|
FlowBlock tryFlow, FlowBlock catchFlow) { |
|
|
|
|
|
|
|
|
|
StructuredBlock catchBlock = catchFlow.block; |
|
|
|
|
static void analyzeCatchBlock(Type type, FlowBlock tryFlow, |
|
|
|
|
StructuredBlock catchBlock) { |
|
|
|
|
|
|
|
|
|
CatchBlock newBlock = new CatchBlock(type); |
|
|
|
|
((TryBlock)tryFlow.block).addCatchBlock(newBlock); |
|
|
|
|
newBlock.setCatchBlock(catchFlow.block); |
|
|
|
|
tryFlow.mergeSuccessors(catchFlow); |
|
|
|
|
tryFlow.mergeAddr(catchFlow); |
|
|
|
|
newBlock.setCatchBlock(catchBlock); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* And now the complicated parts. */ |
|
|
|
@ -128,44 +124,84 @@ public class TransformExceptionHandlers { |
|
|
|
|
* local assignment matches the final ret and then returns. |
|
|
|
|
*/ |
|
|
|
|
boolean transformSubRoutine(StructuredBlock subRoutine) { |
|
|
|
|
if (!(subRoutine instanceof SequentialBlock) |
|
|
|
|
|| !(subRoutine.getSubBlocks()[0] instanceof InstructionBlock)) |
|
|
|
|
if (!(subRoutine instanceof SequentialBlock)) |
|
|
|
|
return false; |
|
|
|
|
SequentialBlock sequBlock = (SequentialBlock) subRoutine; |
|
|
|
|
InstructionBlock instr = (InstructionBlock)sequBlock.subBlocks[0]; |
|
|
|
|
|
|
|
|
|
if (!(instr.getInstruction() instanceof StoreInstruction) |
|
|
|
|
|| !(((StoreInstruction) instr.getInstruction()).getLValue() |
|
|
|
|
instanceof LocalStoreOperator)) |
|
|
|
|
StructuredBlock firstBlock = sequBlock.getSubBlocks()[0]; |
|
|
|
|
|
|
|
|
|
LocalInfo local = null; |
|
|
|
|
if (firstBlock instanceof SpecialBlock) { |
|
|
|
|
SpecialBlock popBlock |
|
|
|
|
= (SpecialBlock) firstBlock; |
|
|
|
|
if (popBlock.type != SpecialBlock.POP |
|
|
|
|
|| popBlock.count != 1) |
|
|
|
|
return false; |
|
|
|
|
} else if (firstBlock instanceof InstructionBlock) { |
|
|
|
|
Expression expr |
|
|
|
|
= ((InstructionBlock) firstBlock).getInstruction(); |
|
|
|
|
if (expr instanceof StoreInstruction |
|
|
|
|
&& ((StoreInstruction) |
|
|
|
|
expr).getLValue() instanceof LocalStoreOperator) { |
|
|
|
|
LocalStoreOperator store = (LocalStoreOperator) |
|
|
|
|
((StoreInstruction)instr.getInstruction()).getLValue(); |
|
|
|
|
((StoreInstruction)expr).getLValue(); |
|
|
|
|
local = store.getLocalInfo(); |
|
|
|
|
expr = ((StoreInstruction) expr).getSubExpressions()[1]; |
|
|
|
|
} |
|
|
|
|
if (!(expr instanceof NopOperator)) |
|
|
|
|
return false; |
|
|
|
|
} else |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
while (sequBlock.subBlocks[1] instanceof SequentialBlock) |
|
|
|
|
sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; |
|
|
|
|
/* We have now committed. Remove the first Statement which |
|
|
|
|
* stores/removes the return address. |
|
|
|
|
*/ |
|
|
|
|
firstBlock.removeBlock(); |
|
|
|
|
|
|
|
|
|
/* XXX - Check that the local isn't used for any other purposes |
|
|
|
|
* than RET and replace any RET with a flow control to end of |
|
|
|
|
* flow block. |
|
|
|
|
/* XXX - Replace any RET with a jump to end of this flow block. |
|
|
|
|
* |
|
|
|
|
* This is a complicated task which isn't needed for javac nor jikes. |
|
|
|
|
* 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, |
|
|
|
|
* as long as the bytecode was verified correctly. |
|
|
|
|
*/ |
|
|
|
|
if (sequBlock.subBlocks[1].jump != null |
|
|
|
|
&& sequBlock.subBlocks[1].jump.destination |
|
|
|
|
== FlowBlock.END_OF_METHOD) { |
|
|
|
|
instr.removeBlock(); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (! (sequBlock.subBlocks[1] instanceof RetBlock) |
|
|
|
|
|| !(((RetBlock)sequBlock.subBlocks[1]) |
|
|
|
|
.local.equals(store.getLocalInfo()))) |
|
|
|
|
return false; |
|
|
|
|
while (sequBlock.subBlocks[1] instanceof SequentialBlock) |
|
|
|
|
sequBlock = (SequentialBlock) sequBlock.subBlocks[1]; |
|
|
|
|
|
|
|
|
|
instr.removeBlock(); |
|
|
|
|
if (sequBlock.subBlocks[1] instanceof RetBlock |
|
|
|
|
&& (((RetBlock) sequBlock.subBlocks[1]).local.equals(local))) { |
|
|
|
|
sequBlock.subBlocks[1].removeBlock(); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Remove the locale that javac introduces to temporary store the return |
|
|
|
|
* value, when it executes a finally block resp. monitorexit |
|
|
|
|
* @param ret the ReturnBlock. |
|
|
|
|
*/ |
|
|
|
|
private void removeReturnLocal(ReturnBlock ret) { |
|
|
|
|
if (ret.outer == null |
|
|
|
|
|| !(ret.outer instanceof SequentialBlock)) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
StructuredBlock pred = ret.outer.getSubBlocks()[0]; |
|
|
|
|
if (!(pred instanceof InstructionBlock)) |
|
|
|
|
return; |
|
|
|
|
Expression instr = ((InstructionBlock) pred).getInstruction(); |
|
|
|
|
if (!(instr instanceof StoreInstruction)) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
Expression retInstr = ret.getInstruction(); |
|
|
|
|
if (!(retInstr instanceof LocalLoadOperator |
|
|
|
|
&& ((StoreInstruction) instr).lvalueMatches |
|
|
|
|
((LocalLoadOperator) retInstr))) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
Expression rvalue = ((StoreInstruction) instr).getSubExpressions()[1]; |
|
|
|
|
ret.setInstruction(rvalue); |
|
|
|
|
ret.replace(ret.outer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Remove the JSR's jumping to the specified subRoutine. It |
|
|
|
|
* is checked if the next block is a leaving instruction, and |
|
|
|
@ -175,16 +211,24 @@ public class TransformExceptionHandlers { |
|
|
|
|
* @param tryFlow the FlowBLock of the try block. |
|
|
|
|
* @param subRoutine the FlowBlock of the sub routine. |
|
|
|
|
*/ |
|
|
|
|
private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) { |
|
|
|
|
for (Jump jumps = tryFlow.removeJumps(subRoutine); |
|
|
|
|
jumps != null; jumps = jumps.next) { |
|
|
|
|
private void removeJSR(FlowBlock tryFlow, StructuredBlock catchBlock, |
|
|
|
|
FlowBlock subRoutine) { |
|
|
|
|
Jump nextJump; |
|
|
|
|
for (Jump jumps = tryFlow.getJumps(subRoutine); |
|
|
|
|
jumps != null; jumps = nextJump) { |
|
|
|
|
|
|
|
|
|
StructuredBlock prev = jumps.prev; |
|
|
|
|
prev.removeJump(); |
|
|
|
|
nextJump = jumps.next; |
|
|
|
|
if (prev instanceof EmptyBlock |
|
|
|
|
&& prev.outer instanceof JsrBlock) { |
|
|
|
|
if (prev.outer == catchBlock) { |
|
|
|
|
/* This is the mandatory jsr in the catch block */ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (prev.outer.getNextFlowBlock() != null) { |
|
|
|
|
/* The jsr is directly before a jump, okay. */ |
|
|
|
|
tryFlow.removeSuccessor(jumps); |
|
|
|
|
prev.removeJump(); |
|
|
|
|
prev.outer.removeBlock(); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
@ -196,6 +240,8 @@ public class TransformExceptionHandlers { |
|
|
|
|
&& seq.subBlocks[1].getSubBlocks()[0] |
|
|
|
|
instanceof JsrBlock)) { |
|
|
|
|
/* The jsr is followed by a jsr, okay. */ |
|
|
|
|
tryFlow.removeSuccessor(jumps); |
|
|
|
|
prev.removeJump(); |
|
|
|
|
prev.outer.removeBlock(); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
@ -203,33 +249,12 @@ public class TransformExceptionHandlers { |
|
|
|
|
&& !(seq.subBlocks[1] instanceof ThrowBlock)) { |
|
|
|
|
|
|
|
|
|
/* The jsr is followed by a return, okay. */ |
|
|
|
|
tryFlow.removeSuccessor(jumps); |
|
|
|
|
prev.removeJump(); |
|
|
|
|
ReturnBlock ret = (ReturnBlock) seq.subBlocks[1]; |
|
|
|
|
prev.outer.removeBlock(); |
|
|
|
|
|
|
|
|
|
if (ret.outer != null |
|
|
|
|
&& ret.outer instanceof SequentialBlock) { |
|
|
|
|
/* Try to eliminate the local that javac uses |
|
|
|
|
* in this case. |
|
|
|
|
*/ |
|
|
|
|
try { |
|
|
|
|
StoreInstruction store = (StoreInstruction) |
|
|
|
|
((InstructionBlock) |
|
|
|
|
ret.outer.getSubBlocks()[0]).instr; |
|
|
|
|
LocalInfo local = |
|
|
|
|
((LocalStoreOperator) store.getLValue()) |
|
|
|
|
.getLocalInfo(); |
|
|
|
|
if (store.lvalueMatches |
|
|
|
|
((LocalLoadOperator) |
|
|
|
|
ret.getInstruction())) { |
|
|
|
|
Expression expr = |
|
|
|
|
store.getSubExpressions()[1]; |
|
|
|
|
ret.setInstruction(expr); |
|
|
|
|
ret.replace(ret.outer); |
|
|
|
|
} |
|
|
|
|
} catch(ClassCastException ex) { |
|
|
|
|
/* didn't succeed */ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
removeReturnLocal(ret); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -239,11 +264,15 @@ public class TransformExceptionHandlers { |
|
|
|
|
*/ |
|
|
|
|
DescriptionBlock msg |
|
|
|
|
= new DescriptionBlock("ERROR: GOTO FINALLY BLOCK!"); |
|
|
|
|
tryFlow.removeSuccessor(jumps); |
|
|
|
|
prev.removeJump(); |
|
|
|
|
prev.appendBlock(msg); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void checkAndRemoveJSR(FlowBlock tryFlow, FlowBlock subRoutine, |
|
|
|
|
public void checkAndRemoveJSR(FlowBlock tryFlow, |
|
|
|
|
StructuredBlock catchBlock, |
|
|
|
|
FlowBlock subRoutine, |
|
|
|
|
int startOutExit, int endOutExit) { |
|
|
|
|
boolean foundSub = false; |
|
|
|
|
Iterator iter = tryFlow.getSuccessors().iterator(); |
|
|
|
@ -336,7 +365,7 @@ public class TransformExceptionHandlers { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (foundSub) |
|
|
|
|
removeJSR(tryFlow, subRoutine); |
|
|
|
|
removeJSR(tryFlow, catchBlock, subRoutine); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static boolean isMonitorExit(Expression instr, LocalInfo local) { |
|
|
|
@ -366,9 +395,10 @@ public class TransformExceptionHandlers { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void checkAndRemoveMonitorExit(FlowBlock tryFlow, LocalInfo local, |
|
|
|
|
int startOutExit, int endOutExit, |
|
|
|
|
int startMonExit, int endMonExit) { |
|
|
|
|
public void checkAndRemoveMonitorExit(FlowBlock tryFlow, |
|
|
|
|
StructuredBlock catchBlock, |
|
|
|
|
LocalInfo local, |
|
|
|
|
int start, int end) { |
|
|
|
|
FlowBlock subRoutine = null; |
|
|
|
|
Iterator succs = tryFlow.getSuccessors().iterator(); |
|
|
|
|
dest_loop: |
|
|
|
@ -421,11 +451,15 @@ public class TransformExceptionHandlers { |
|
|
|
|
if (pred instanceof InstructionBlock) { |
|
|
|
|
Expression instr = |
|
|
|
|
((InstructionBlock)pred).getInstruction(); |
|
|
|
|
if (isMonitorExit(instr, local)) |
|
|
|
|
if (isMonitorExit(instr, local)) { |
|
|
|
|
pred.removeBlock(); |
|
|
|
|
if (prev instanceof ReturnBlock) |
|
|
|
|
removeReturnLocal((ReturnBlock) prev); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (prev instanceof InstructionBlock |
|
|
|
|
&& isMonitorExit(((InstructionBlock)prev).instr, local)) { |
|
|
|
@ -448,9 +482,9 @@ public class TransformExceptionHandlers { |
|
|
|
|
*/ |
|
|
|
|
if (prev instanceof JsrBlock) { |
|
|
|
|
if (subRoutine == null |
|
|
|
|
&& successor.getAddr() >= startMonExit |
|
|
|
|
&& successor.getNextAddr() <= endMonExit) { |
|
|
|
|
successor.analyze(startMonExit, endMonExit); |
|
|
|
|
&& successor.getAddr() >= start |
|
|
|
|
&& successor.getNextAddr() <= end) { |
|
|
|
|
successor.analyze(start, end); |
|
|
|
|
|
|
|
|
|
if (isMonitorExitSubRoutine(successor, local)) |
|
|
|
|
subRoutine = successor; |
|
|
|
@ -467,9 +501,9 @@ public class TransformExceptionHandlers { |
|
|
|
|
* block. |
|
|
|
|
*/ |
|
|
|
|
if (successor.predecessors.size() == 1 |
|
|
|
|
&& successor.getAddr() >= startOutExit |
|
|
|
|
&& successor.getNextAddr() <= endOutExit) { |
|
|
|
|
successor.analyze(startOutExit, endOutExit); |
|
|
|
|
&& successor.getAddr() >= start |
|
|
|
|
&& successor.getNextAddr() <= end) { |
|
|
|
|
successor.analyze(start, end); |
|
|
|
|
|
|
|
|
|
StructuredBlock sb = successor.block; |
|
|
|
|
if (sb instanceof SequentialBlock) |
|
|
|
@ -479,9 +513,9 @@ public class TransformExceptionHandlers { |
|
|
|
|
StructuredBlock jsrInner = sb.getSubBlocks()[0]; |
|
|
|
|
FlowBlock dest = jsrInner.jump.destination; |
|
|
|
|
if (subRoutine == null |
|
|
|
|
&& dest.getAddr() >= startMonExit |
|
|
|
|
&& dest.getNextAddr() <= endMonExit) { |
|
|
|
|
dest.analyze(startMonExit, endMonExit); |
|
|
|
|
&& dest.getAddr() >= start |
|
|
|
|
&& dest.getNextAddr() <= end) { |
|
|
|
|
dest.analyze(start, end); |
|
|
|
|
if (isMonitorExitSubRoutine(dest, local)) |
|
|
|
|
subRoutine = dest; |
|
|
|
|
} |
|
|
|
@ -512,30 +546,62 @@ public class TransformExceptionHandlers { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (subRoutine != null) { |
|
|
|
|
removeJSR(tryFlow, subRoutine); |
|
|
|
|
removeJSR(tryFlow, catchBlock, subRoutine); |
|
|
|
|
tryFlow.mergeAddr(subRoutine); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private StoreInstruction getExceptionStore(StructuredBlock catchBlock) { |
|
|
|
|
if (!(catchBlock instanceof SequentialBlock) |
|
|
|
|
|| !(catchBlock.getSubBlocks()[0] instanceof InstructionBlock)) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
Expression instr = |
|
|
|
|
((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction(); |
|
|
|
|
if (!(instr instanceof StoreInstruction)) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
StoreInstruction store = (StoreInstruction) instr; |
|
|
|
|
if (!(store.getLValue() instanceof LocalStoreOperator |
|
|
|
|
&& store.getSubExpressions()[1] instanceof NopOperator)) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
return store; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean analyzeSynchronized(FlowBlock tryFlow, |
|
|
|
|
FlowBlock catchFlow, |
|
|
|
|
StructuredBlock catchBlock, |
|
|
|
|
int endHandler) { |
|
|
|
|
if (!(catchFlow.block instanceof SequentialBlock |
|
|
|
|
&& catchFlow.block.getSubBlocks()[0] |
|
|
|
|
StoreInstruction excStore = getExceptionStore(catchBlock); |
|
|
|
|
if (excStore != null) |
|
|
|
|
catchBlock = catchBlock.getSubBlocks()[1]; |
|
|
|
|
|
|
|
|
|
if (!(catchBlock instanceof SequentialBlock |
|
|
|
|
&& catchBlock.getSubBlocks()[0] |
|
|
|
|
instanceof InstructionBlock)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
SequentialBlock catchBlock = (SequentialBlock) catchFlow.block; |
|
|
|
|
Expression instr = |
|
|
|
|
((InstructionBlock)catchBlock.subBlocks[0]).getInstruction(); |
|
|
|
|
((InstructionBlock)catchBlock.getSubBlocks()[0]).getInstruction(); |
|
|
|
|
|
|
|
|
|
if (instr instanceof MonitorExitOperator |
|
|
|
|
if (!(instr instanceof MonitorExitOperator |
|
|
|
|
&& instr.getFreeOperandCount() == 0 |
|
|
|
|
&& (((MonitorExitOperator)instr).getSubExpressions()[0] |
|
|
|
|
instanceof LocalLoadOperator) |
|
|
|
|
&& catchBlock.subBlocks[1] instanceof ThrowBlock |
|
|
|
|
&& (((ThrowBlock)catchBlock.subBlocks[1]).instr |
|
|
|
|
instanceof NopOperator)) { |
|
|
|
|
&& catchBlock.getSubBlocks()[1] instanceof ThrowBlock)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
Expression throwInstr = |
|
|
|
|
((ThrowBlock)catchBlock.getSubBlocks()[1]).getInstruction(); |
|
|
|
|
|
|
|
|
|
if (excStore != null) { |
|
|
|
|
if (!(throwInstr instanceof Operator |
|
|
|
|
&& excStore.lvalueMatches((Operator)throwInstr))) |
|
|
|
|
return false; |
|
|
|
|
} else { |
|
|
|
|
if (!(throwInstr instanceof NopOperator)) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* This is a synchronized block: |
|
|
|
|
* |
|
|
|
@ -549,23 +615,21 @@ public class TransformExceptionHandlers { |
|
|
|
|
* |- monitorexit local_x | |
|
|
|
|
* ` jump after this block (without jsr monexit) | |
|
|
|
|
* | |
|
|
|
|
* catchFlow: | |
|
|
|
|
* catchBlock: | |
|
|
|
|
* local_n = stack | |
|
|
|
|
* monitorexit local_x | |
|
|
|
|
* throw local_n | |
|
|
|
|
* [OR ALTERNATIVELY:] | |
|
|
|
|
* monitorexit local_x | |
|
|
|
|
* throw stack | |
|
|
|
|
* optional subroutine: <-----------------------------------' |
|
|
|
|
* astore_n |
|
|
|
|
* monitorexit local_x |
|
|
|
|
* return_n |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/* Now remove the jump (after the throw) from the |
|
|
|
|
* catch block so that we can forget about it. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
catchFlow.removeSuccessor(catchBlock.subBlocks[1].jump); |
|
|
|
|
MonitorExitOperator monexit = (MonitorExitOperator) |
|
|
|
|
((InstructionBlock) catchBlock.subBlocks[0]).instr; |
|
|
|
|
((InstructionBlock) catchBlock.getSubBlocks()[0]).instr; |
|
|
|
|
LocalInfo local = |
|
|
|
|
((LocalLoadOperator)monexit.getSubExpressions()[0]) |
|
|
|
|
.getLocalInfo(); |
|
|
|
@ -574,15 +638,11 @@ public class TransformExceptionHandlers { |
|
|
|
|
& GlobalOptions.DEBUG_ANALYZE) != 0) |
|
|
|
|
GlobalOptions.err.println |
|
|
|
|
("analyzeSynchronized(" + tryFlow.getAddr() |
|
|
|
|
+ "," + tryFlow.getNextAddr() + "," + catchFlow.getAddr() |
|
|
|
|
+ "," + catchFlow.getNextAddr() + "," + endHandler + ")"); |
|
|
|
|
+ "," + tryFlow.getNextAddr() + "," + endHandler + ")"); |
|
|
|
|
|
|
|
|
|
checkAndRemoveMonitorExit |
|
|
|
|
(tryFlow, local, |
|
|
|
|
tryFlow.getNextAddr(), catchFlow.getAddr(), |
|
|
|
|
catchFlow.getNextAddr(), endHandler); |
|
|
|
|
(tryFlow, catchBlock, local, tryFlow.getNextAddr(), endHandler); |
|
|
|
|
|
|
|
|
|
tryFlow.mergeAddr(catchFlow); |
|
|
|
|
SynchronizedBlock syncBlock = new SynchronizedBlock(local); |
|
|
|
|
TryBlock tryBlock = (TryBlock) tryFlow.block; |
|
|
|
|
syncBlock.replace(tryBlock); |
|
|
|
@ -592,11 +652,9 @@ public class TransformExceptionHandlers { |
|
|
|
|
tryFlow.lastModified = syncBlock; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean analyzeFinally(FlowBlock tryFlow, FlowBlock catchFlow, |
|
|
|
|
int end) { |
|
|
|
|
private boolean analyzeFinally(FlowBlock tryFlow, |
|
|
|
|
StructuredBlock catchBlock, int end) { |
|
|
|
|
|
|
|
|
|
/* Layout of a try-finally block: |
|
|
|
|
* |
|
|
|
@ -608,7 +666,7 @@ public class TransformExceptionHandlers { |
|
|
|
|
* | jsr finally -----------------, |
|
|
|
|
* `- jump after finally | |
|
|
|
|
* | |
|
|
|
|
* catchFlow: (already checked) | |
|
|
|
|
* catchBlock: | |
|
|
|
|
* local_n = stack v |
|
|
|
|
* jsr finally ---------------->| |
|
|
|
|
* throw local_n; | |
|
|
|
@ -618,20 +676,17 @@ public class TransformExceptionHandlers { |
|
|
|
|
* return_n |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
if (!(catchFlow.block instanceof SequentialBlock) |
|
|
|
|
|| !(catchFlow.block.getSubBlocks()[0] |
|
|
|
|
instanceof InstructionBlock) |
|
|
|
|
|| !(catchFlow.block.getSubBlocks()[1] |
|
|
|
|
instanceof SequentialBlock)) |
|
|
|
|
StoreInstruction excStore = getExceptionStore(catchBlock); |
|
|
|
|
if (excStore == null) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
catchBlock = catchBlock.getSubBlocks()[1]; |
|
|
|
|
if (!(catchBlock instanceof SequentialBlock)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
StructuredBlock finallyBlock = null; |
|
|
|
|
SequentialBlock catchBlock = (SequentialBlock) catchFlow.block; |
|
|
|
|
Expression instr = |
|
|
|
|
((InstructionBlock)catchBlock.subBlocks[0]).getInstruction(); |
|
|
|
|
catchBlock = (SequentialBlock)catchBlock.subBlocks[1]; |
|
|
|
|
|
|
|
|
|
if (catchBlock.subBlocks[0] instanceof LoopBlock) { |
|
|
|
|
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: |
|
|
|
@ -641,20 +696,21 @@ public class TransformExceptionHandlers { |
|
|
|
|
* break; |
|
|
|
|
* throw local_x |
|
|
|
|
* } while(false); |
|
|
|
|
* finallyBlock; |
|
|
|
|
* finallyBlock; (starts with POP or local_y = POP) |
|
|
|
|
*/ |
|
|
|
|
LoopBlock doWhileFalse = (LoopBlock)catchBlock.subBlocks[0]; |
|
|
|
|
LoopBlock doWhileFalse = (LoopBlock)catchBlock.getSubBlocks()[0]; |
|
|
|
|
if (doWhileFalse.type == LoopBlock.DOWHILE |
|
|
|
|
&& doWhileFalse.cond == LoopBlock.FALSE |
|
|
|
|
&& doWhileFalse.bodyBlock instanceof SequentialBlock) { |
|
|
|
|
finallyBlock = catchBlock.subBlocks[1]; |
|
|
|
|
if (transformSubRoutine(catchBlock.getSubBlocks()[1])) { |
|
|
|
|
finallyBlock = catchBlock.getSubBlocks()[1]; |
|
|
|
|
catchBlock = (SequentialBlock) doWhileFalse.bodyBlock; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!(catchBlock instanceof SequentialBlock |
|
|
|
|
&& catchBlock.getSubBlocks()[0] instanceof JsrBlock |
|
|
|
|
&& instr instanceof StoreInstruction |
|
|
|
|
&& catchBlock.getSubBlocks()[1] instanceof ThrowBlock)) |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
@ -663,21 +719,12 @@ public class TransformExceptionHandlers { |
|
|
|
|
ThrowBlock throwBlock = (ThrowBlock) catchBlock.getSubBlocks()[1]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!(throwBlock.instr instanceof LocalLoadOperator) |
|
|
|
|
|| !(((StoreInstruction) instr).lvalueMatches |
|
|
|
|
((LocalLoadOperator) throwBlock.instr))) |
|
|
|
|
if (!(throwBlock.getInstruction() instanceof Operator |
|
|
|
|
&& excStore.lvalueMatches((Operator) |
|
|
|
|
throwBlock.getInstruction()))) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
/* Wow that was complicated :-) |
|
|
|
|
* But now we know that the catch block looks |
|
|
|
|
* exactly like it should: |
|
|
|
|
* |
|
|
|
|
* catchBlock: |
|
|
|
|
* JSR |
|
|
|
|
* finally |
|
|
|
|
* throw local_n <- matches the local in instr. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
FlowBlock subRoutine; |
|
|
|
|
if (finallyBlock != null) { |
|
|
|
|
/* Check if the jsr breaks (see two comments above). We don't |
|
|
|
|
* need to check if it breaks to the right block, because |
|
|
|
@ -687,132 +734,100 @@ public class TransformExceptionHandlers { |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
/* Check if the try block has no exit (except throws) |
|
|
|
|
* XXX - Unfortunately the try block already has the |
|
|
|
|
* successors of catch block. |
|
|
|
|
*/ |
|
|
|
|
Set succs = tryFlow.getSuccessors(); |
|
|
|
|
if (succs.size() > 0) { |
|
|
|
|
if (!succs.contains(FlowBlock.END_OF_METHOD)) |
|
|
|
|
return false; |
|
|
|
|
Jump throwJumps |
|
|
|
|
= tryFlow.getJumps(FlowBlock.END_OF_METHOD); |
|
|
|
|
for (/**/; throwJumps != null; |
|
|
|
|
throwJumps = throwJumps.next) { |
|
|
|
|
if (!(throwJumps.prev instanceof ThrowBlock)) |
|
|
|
|
/* There is a return exit in the try block */ |
|
|
|
|
// if (tryFlow.getSuccessors() > 0)
|
|
|
|
|
// return false;
|
|
|
|
|
|
|
|
|
|
catchBlock = finallyBlock; |
|
|
|
|
subRoutine = null; |
|
|
|
|
} else { |
|
|
|
|
if (!(jsrBlock.innerBlock instanceof EmptyBlock)) |
|
|
|
|
return false; |
|
|
|
|
catchBlock = jsrBlock; |
|
|
|
|
subRoutine = jsrBlock.innerBlock.jump.destination; |
|
|
|
|
checkAndRemoveJSR(tryFlow, catchBlock, subRoutine, |
|
|
|
|
tryFlow.getNextAddr(), subRoutine.getAddr()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* Remove the jump of the throw instruction. |
|
|
|
|
*/ |
|
|
|
|
catchFlow.removeSuccessor(throwBlock.jump); |
|
|
|
|
throwBlock.removeJump(); |
|
|
|
|
|
|
|
|
|
/* Replace the catchBlock with the finallyBlock. |
|
|
|
|
/* 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.replace(catchFlow.block); |
|
|
|
|
transformSubRoutine(finallyBlock); |
|
|
|
|
|
|
|
|
|
tryFlow.updateInOutCatch(catchFlow); |
|
|
|
|
tryFlow.mergeAddr(catchFlow); |
|
|
|
|
finallyBlock = catchFlow.block; |
|
|
|
|
tryFlow.mergeSuccessors(catchFlow); |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
FlowBlock subRoutine = jsrBlock.innerBlock.jump.destination; |
|
|
|
|
checkAndRemoveJSR(tryFlow, subRoutine, |
|
|
|
|
tryFlow.getNextAddr(), catchFlow.getAddr()); |
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
while (subRoutine.analyze(catchFlow.getNextAddr(), end)); |
|
|
|
|
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 |
|
|
|
|
&& transformSubRoutine(subRoutine.block)) { |
|
|
|
|
subRoutine.mergeAddr(catchFlow); |
|
|
|
|
|
|
|
|
|
/* Now remove the jump to the JSR from the catch block |
|
|
|
|
* and the jump of the throw instruction. |
|
|
|
|
*/ |
|
|
|
|
catchFlow.removeSuccessor(jsrBlock.innerBlock.jump); |
|
|
|
|
jsrBlock.innerBlock.removeJump(); |
|
|
|
|
catchFlow.removeSuccessor(throwBlock.jump); |
|
|
|
|
throwBlock.removeJump(); |
|
|
|
|
|
|
|
|
|
tryFlow.updateInOutCatch(subRoutine); |
|
|
|
|
tryFlow.removeSuccessor(jsrBlock.innerBlock.jump); |
|
|
|
|
tryFlow.mergeAddr(subRoutine); |
|
|
|
|
tryFlow.mergeSuccessors(subRoutine); |
|
|
|
|
finallyBlock = subRoutine.block; |
|
|
|
|
} else { |
|
|
|
|
catchFlow.removeSuccessor(throwBlock.jump); |
|
|
|
|
throwBlock.removeJump(); |
|
|
|
|
finallyBlock = jsrBlock; |
|
|
|
|
|
|
|
|
|
finallyBlock.replace(catchFlow.block); |
|
|
|
|
transformSubRoutine(finallyBlock); |
|
|
|
|
|
|
|
|
|
tryFlow.updateInOutCatch(catchFlow); |
|
|
|
|
tryFlow.mergeAddr(catchFlow); |
|
|
|
|
finallyBlock = catchFlow.block; |
|
|
|
|
tryFlow.mergeSuccessors(catchFlow); |
|
|
|
|
} |
|
|
|
|
subRoutine.block.replace(catchBlock); |
|
|
|
|
tryFlow.updateInOutCatch(subRoutine); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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(finallyBlock); |
|
|
|
|
tryBlock.addCatchBlock(newBlock); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean analyzeSpecialFinally(FlowBlock tryFlow, |
|
|
|
|
FlowBlock catchFlow, int end) { |
|
|
|
|
|
|
|
|
|
StructuredBlock catchBlock, |
|
|
|
|
int end) { |
|
|
|
|
StructuredBlock firstInstr = |
|
|
|
|
catchFlow.block instanceof SequentialBlock |
|
|
|
|
? catchFlow.block.getSubBlocks()[0]: catchFlow.block; |
|
|
|
|
catchBlock instanceof SequentialBlock |
|
|
|
|
? catchBlock.getSubBlocks()[0]: catchBlock; |
|
|
|
|
|
|
|
|
|
if (firstInstr instanceof SpecialBlock |
|
|
|
|
if (!(firstInstr instanceof SpecialBlock |
|
|
|
|
&& ((SpecialBlock)firstInstr).type == SpecialBlock.POP |
|
|
|
|
&& ((SpecialBlock)firstInstr).count == 1) { |
|
|
|
|
&& ((SpecialBlock)firstInstr).count == 1)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
/* This is a special try/finally-block, where |
|
|
|
|
/* This may be a special try/finally-block, where |
|
|
|
|
* the finally block ends with a break, return or |
|
|
|
|
* similar. |
|
|
|
|
*/ |
|
|
|
|
FlowBlock succ = null; |
|
|
|
|
|
|
|
|
|
FlowBlock succ = (firstInstr.jump != null) ? |
|
|
|
|
firstInstr.jump.destination : null; |
|
|
|
|
boolean hasExit = false; |
|
|
|
|
Iterator iter = tryFlow.getSuccessors().iterator(); |
|
|
|
|
while (iter.hasNext()) { |
|
|
|
|
FlowBlock dest = (FlowBlock) iter.next(); |
|
|
|
|
if (dest == succ) |
|
|
|
|
continue; |
|
|
|
|
if (dest != FlowBlock.END_OF_METHOD) { |
|
|
|
|
/* There is another exit in the try block, bad */ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
for (Jump throwJumps = (Jump) tryFlow.getJumps(dest); |
|
|
|
|
throwJumps != null; throwJumps = throwJumps.next) { |
|
|
|
|
if (!(throwJumps.prev instanceof ThrowBlock)) { |
|
|
|
|
/* There is a return exit in the try block */ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* remove the pop now */ |
|
|
|
|
if (catchBlock instanceof SequentialBlock) |
|
|
|
|
catchBlock = catchBlock.getSubBlocks()[1]; |
|
|
|
|
else { |
|
|
|
|
catchBlock = new EmptyBlock(); |
|
|
|
|
catchBlock.moveJump(firstInstr.jump); |
|
|
|
|
|
|
|
|
|
succ = firstInstr.jump.destination; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* remove the pop now */ |
|
|
|
|
firstInstr.removeBlock(); |
|
|
|
|
tryFlow.mergeAddr(catchFlow); |
|
|
|
|
// 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); |
|
|
|
@ -833,24 +848,9 @@ public class TransformExceptionHandlers { |
|
|
|
|
} |
|
|
|
|
FinallyBlock newBlock = new FinallyBlock(); |
|
|
|
|
tryBlock.addCatchBlock(newBlock); |
|
|
|
|
/* try block has no successors */ |
|
|
|
|
|
|
|
|
|
if (succ != null && succ.predecessors.size() == 1) { |
|
|
|
|
while (succ.analyze(catchFlow.getNextAddr(), end)); |
|
|
|
|
tryFlow.mergeAddr(succ); |
|
|
|
|
tryFlow.removeJumps(succ); |
|
|
|
|
newBlock.setCatchBlock(succ.block); |
|
|
|
|
tryFlow.mergeSuccessors(succ); |
|
|
|
|
} else { |
|
|
|
|
/* Put the catchBlock in instead. |
|
|
|
|
*/ |
|
|
|
|
newBlock.setCatchBlock(catchFlow.block); |
|
|
|
|
tryFlow.mergeSuccessors(catchFlow); |
|
|
|
|
} |
|
|
|
|
newBlock.setCatchBlock(catchBlock); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Analyzes all exception handlers to try/catch/finally or |
|
|
|
@ -959,16 +959,21 @@ public class TransformExceptionHandlers { |
|
|
|
|
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); |
|
|
|
|
analyzeCatchBlock(exc.type, tryFlow, catchFlow.block); |
|
|
|
|
|
|
|
|
|
else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler) |
|
|
|
|
&& ! analyzeFinally(tryFlow, catchFlow, endHandler) |
|
|
|
|
&& ! analyzeSpecialFinally(tryFlow, catchFlow, |
|
|
|
|
else if (!analyzeSynchronized(tryFlow, catchFlow.block, |
|
|
|
|
endHandler) |
|
|
|
|
&& ! analyzeFinally(tryFlow, catchFlow.block, |
|
|
|
|
endHandler) |
|
|
|
|
&& ! analyzeSpecialFinally(tryFlow, catchFlow.block, |
|
|
|
|
endHandler)) |
|
|
|
|
|
|
|
|
|
analyzeCatchBlock(Type.tObject, tryFlow, catchFlow); |
|
|
|
|
analyzeCatchBlock(Type.tObject, tryFlow, catchFlow.block); |
|
|
|
|
|
|
|
|
|
tryFlow.checkConsistent(); |
|
|
|
|
if ((GlobalOptions.debuggingFlags |
|
|
|
|