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

@ -1,4 +1,4 @@
/* FlowBlock Copyright (C) 1998-1999 Jochen Hoenicke.
/* FlowBlock Copyright (C) 1998-2001 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -748,7 +748,8 @@ public class FlowBlock {
StructuredBlock last = lastModified;
while (last.outer instanceof SequentialBlock
|| last.outer instanceof TryBlock)
|| last.outer instanceof TryBlock
|| last.outer instanceof FinallyBlock)
last = last.outer;
if (last.outer != null)
throw new AssertError("Inconsistency");

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

Loading…
Cancel
Save