Try catch blocks clean up

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@78 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 26 years ago
parent e2b966818c
commit dff23235cc
  1. 76
      jode/jode/decompiler/CodeAnalyzer.java
  2. 80
      jode/jode/flow/CatchBlock.java
  3. 3
      jode/jode/flow/CombineIfGotoExpressions.java
  4. 6
      jode/jode/flow/CompleteSynchronized.java
  5. 9
      jode/jode/flow/CreateAssignExpression.java
  6. 7
      jode/jode/flow/CreateConstantArray.java
  7. 3
      jode/jode/flow/CreateExpression.java
  8. 3
      jode/jode/flow/CreateForInitializer.java
  9. 9
      jode/jode/flow/CreateIfThenElseOperator.java
  10. 6
      jode/jode/flow/CreateNewConstructor.java
  11. 6
      jode/jode/flow/CreatePrePostIncExpression.java
  12. 5
      jode/jode/flow/EmptyBlock.java
  13. 587
      jode/jode/flow/FlowBlock.java
  14. 3
      jode/jode/flow/InstructionContainer.java
  15. 2
      jode/jode/flow/LoopBlock.java
  16. 6
      jode/jode/flow/RemoveEmpty.java
  17. 3
      jode/jode/flow/RetBlock.java
  18. 24
      jode/jode/flow/StructuredBlock.java
  19. 812
      jode/jode/flow/TransformExceptionHandlers.java
  20. 103
      jode/jode/flow/TryBlock.java

@ -19,9 +19,7 @@
package jode;
import jode.flow.FlowBlock;
import jode.flow.Jump;
import jode.flow.StructuredBlock;
import jode.flow.RawTryCatchBlock;
import jode.flow.TransformExceptionHandlers;
import java.util.Stack;
import java.util.Vector;
@ -37,7 +35,9 @@ import gnu.bytecode.CpoolClass;
public class CodeAnalyzer implements Analyzer {
TransformExceptionHandlers handler;
FlowBlock methodHeader;
CodeAttr code;
MethodAnalyzer method;
public JodeEnvironment env;
@ -51,6 +51,19 @@ public class CodeAnalyzer implements Analyzer {
*/
public MethodAnalyzer getMethod() {return method;}
public CodeAnalyzer(MethodAnalyzer ma, CodeAttr bc, JodeEnvironment e)
throws ClassFormatError
{
code = bc;
method = ma;
env = e;
int paramCount = method.getParamCount();
param = new jode.flow.VariableSet();
for (int i=0; i<paramCount; i++)
param.addElement(getLocalInfo(0, i));
}
void readCode(CodeAttr bincode)
throws ClassFormatError
{
@ -76,10 +89,15 @@ public class CodeAnalyzer implements Analyzer {
throw new ClassFormatError(ex.toString());
}
for (int addr=0; addr<instr.length; ) {
instr[addr].resolveJumps(instr);
addr = instr[addr].getNextAddr();
}
handler = new TransformExceptionHandlers(instr);
short[] handlers = gnu.bytecode.Spy.getExceptionHandlers(bincode);
for (int i=0; i<handlers.length; i += 4) {
FlowBlock startBlock = instr[handlers[i + 0]];
for (int i=0; i<handlers.length; i += 4) {
Type type = null;
if (handlers[i + 3 ] != 0) {
CpoolClass cpcls = (CpoolClass)
@ -87,24 +105,23 @@ public class CodeAnalyzer implements Analyzer {
type = Type.tClass(cpcls.getName().getString());
}
instr[handlers[i + 0]] =
new RawTryCatchBlock(type,
new Jump(instr[handlers[i + 1]]),
new Jump(instr[handlers[i + 2]])
).chainTo(instr[handlers[i + 0]]);
}
for (int addr=0; addr<instr.length; ) {
instr[addr].resolveJumps(instr);
addr = instr[addr].getNextAddr();
handler.addHandler(handlers[i + 0], handlers[i + 1],
handlers[i + 2], type);
}
methodHeader = instr[0];
methodHeader.makeStartBlock();
}
int paramCount = method.getParamCount();
param = new jode.flow.VariableSet();
for (int i=0; i<paramCount; i++)
param.addElement(getLocalInfo(0, i));
public void analyze()
{
readCode(code);
handler.analyze();
methodHeader.analyze();
Enumeration enum = allLocals.elements();
while (enum.hasMoreElements()) {
LocalInfo li = (LocalInfo)enum.nextElement();
if (!li.isShadow())
li.getType().useType();
}
}
public void dumpSource(TabbedPrintWriter writer)
@ -114,14 +131,6 @@ public class CodeAnalyzer implements Analyzer {
methodHeader.dumpSource(writer);
}
public CodeAnalyzer(MethodAnalyzer ma, CodeAttr bc, JodeEnvironment e)
throws ClassFormatError
{
method = ma;
env = e;
readCode(bc);
}
public LocalInfo getLocalInfo(int addr, int slot) {
LocalInfo li = (lvt != null)
? lvt.getLocal(slot).getInfo(addr)
@ -135,17 +144,6 @@ public class CodeAnalyzer implements Analyzer {
return (LocalInfo) param.elementAt(slot);
}
public void analyze()
{
methodHeader.analyze();
Enumeration enum = allLocals.elements();
while (enum.hasMoreElements()) {
LocalInfo li = (LocalInfo)enum.nextElement();
if (!li.isShadow())
li.getType().useType();
}
}
public void useClass(Class clazz)
{
env.useClass(clazz);

@ -19,8 +19,6 @@
package jode.flow;
import jode.Type;
import jode.LocalInfo;
import jode.Expression;
import jode.PopOperator;
/**
*
@ -28,11 +26,6 @@ import jode.PopOperator;
*/
public class CatchBlock extends StructuredBlock {
/**
* The try block. This may be another CatchBlock!
*/
StructuredBlock tryBlock;
/**
* The catch block.
*/
@ -48,18 +41,16 @@ public class CatchBlock extends StructuredBlock {
*/
LocalInfo exceptionLocal;
public CatchBlock(Type type) {
exceptionType = type;
CatchBlock() {
}
static int serialno=0;
public void setTryBlock(StructuredBlock tryBlock) {
this.tryBlock = tryBlock;
tryBlock.outer = this;
tryBlock.setFlowBlock(flowBlock);
public CatchBlock(Type type, LocalInfo local) {
exceptionType = type;
exceptionLocal = local;
used.addElement(exceptionLocal);
}
/**
* Sets the catch block.
* @param catchBlock the catch block.
@ -68,37 +59,8 @@ public class CatchBlock extends StructuredBlock {
this.catchBlock = catchBlock;
catchBlock.outer = this;
catchBlock.setFlowBlock(flowBlock);
StructuredBlock firstInstr = (catchBlock instanceof SequentialBlock)
? catchBlock.getSubBlocks()[0] : catchBlock;
if (firstInstr instanceof InstructionBlock) {
Expression instr =
((InstructionBlock) firstInstr).getInstruction();
if (instr instanceof PopOperator
&& ((PopOperator) instr).getCount() == 1) {
/* The exception is ignored. Create a dummy local for it */
exceptionLocal = new LocalInfo(-1);
exceptionLocal.setName("exception_"+(serialno++)+"_");
} else if (instr instanceof jode.LocalStoreOperator) {
/* The exception is stored in a local variable */
exceptionLocal =
((jode.LocalStoreOperator) instr).getLocalInfo();
}
}
if (exceptionLocal != null)
firstInstr.removeBlock();
else {
exceptionLocal = new LocalInfo(-1);
exceptionLocal.setName("ERROR!!!");
}
exceptionLocal.setType(exceptionType);
used.addElement(exceptionLocal);
}
/* The implementation of getNext[Flow]Block is the standard
* implementation */
@ -110,9 +72,7 @@ public class CatchBlock extends StructuredBlock {
*/
public boolean replaceSubBlock(StructuredBlock oldBlock,
StructuredBlock newBlock) {
if (tryBlock == oldBlock)
tryBlock = newBlock;
else if (catchBlock == oldBlock)
if (catchBlock == oldBlock)
catchBlock = newBlock;
else
return false;
@ -123,17 +83,7 @@ public class CatchBlock extends StructuredBlock {
* Returns all sub block of this structured block.
*/
public StructuredBlock[] getSubBlocks() {
return new StructuredBlock[] { tryBlock, catchBlock };
}
/**
* Determines if there is a sub block, that flows through to the end
* of this block. If this returns true, you know that jump is null.
* @return true, if the jump may be safely changed.
*/
public boolean jumpMayBeChanged() {
return ( tryBlock.jump != null || tryBlock.jumpMayBeChanged())
&& (catchBlock.jump != null || catchBlock.jumpMayBeChanged());
return new StructuredBlock[] { catchBlock };
}
/**
@ -154,20 +104,10 @@ public class CatchBlock extends StructuredBlock {
public void dumpInstruction(jode.TabbedPrintWriter writer)
throws java.io.IOException {
/* avoid ugly nested tries */
if (!(outer instanceof CatchBlock)) {
writer.println("try {");
writer.tab();
}
tryBlock.dumpSource(writer);
writer.untab();
writer.println("} catch ("+exceptionType.toString() + " "
+ exceptionLocal.getName().toString()+ ") {");
writer.tab();
catchBlock.dumpSource(writer);
if (!(outer instanceof CatchBlock)) {
writer.untab();
writer.println("}");
}
writer.untab();
}
}

@ -92,7 +92,8 @@ public class CombineIfGotoExpressions implements Transformation{
new ComplexExpression
(new BinaryOperator(Type.tBoolean, operator), e);
cb.setInstruction(cond);
cb.replace(sequBlock, cb);
cb.moveDefinitions(sequBlock, cb);
cb.replace(sequBlock);
return true;
}
return false;

@ -59,7 +59,8 @@ public class CompleteSynchronized implements Transformation {
System.err.print('s');
synBlock.isEntered = true;
synBlock.replace(synBlock.outer, synBlock);
synBlock.moveDefinitions(synBlock.outer,synBlock);
synBlock.replace(synBlock.outer);
/* Is there another expression? */
if (synBlock.outer == null)
@ -84,7 +85,8 @@ public class CompleteSynchronized implements Transformation {
}
synBlock.object = object;
synBlock.replace(synBlock.outer, synBlock);
synBlock.moveDefinitions(synBlock.outer,synBlock);
synBlock.replace(synBlock.outer);
return true;
}
}

@ -83,7 +83,8 @@ public class CreateAssignExpression implements Transformation{
}
((InstructionBlock)opBlock.subBlocks[0])
.setInstruction(rightHandSide);
opBlock.replace(sequBlock, opBlock);
opBlock.moveDefinitions(sequBlock, opBlock);
opBlock.replace(sequBlock);
store.setOperatorIndex(store.OPASSIGN_OP+binop.getOperatorIndex());
store.setLValueType(binop.getType()
@ -94,7 +95,8 @@ public class CreateAssignExpression implements Transformation{
(new AssignOperator(store.getOperatorIndex(), store));
else
lastBlock.setInstruction(store);
lastBlock.replace(opBlock.subBlocks[1], lastBlock);
lastBlock.moveDefinitions(opBlock.subBlocks[1], lastBlock);
lastBlock.replace(opBlock.subBlocks[1]);
return true;
}
@ -120,7 +122,8 @@ public class CreateAssignExpression implements Transformation{
}
lastBlock.setInstruction
(new AssignOperator(store.getOperatorIndex(), store));
lastBlock.replace(sequBlock, lastBlock);
lastBlock.moveDefinitions(sequBlock, lastBlock);
lastBlock.replace(sequBlock);
return true;
}

@ -119,8 +119,11 @@ public class CreateConstantArray implements Transformation {
(new ComplexExpression
(new ConstantArrayOperator(type, consts.length),
consts));
lastBlock.replace(sequBlock.subBlocks[0], lastBlock);
flow.lastModified.replace(sequBlock.subBlocks[1], flow.lastModified);
lastBlock.moveDefinitions(sequBlock.subBlocks[0], lastBlock);
lastBlock.replace(sequBlock.subBlocks[0]);
flow.lastModified.moveDefinitions(sequBlock.subBlocks[1],
flow.lastModified);
flow.lastModified.replace(sequBlock.subBlocks[1]);
return true;
}
}

@ -110,7 +110,8 @@ public class CreateExpression implements Transformation {
System.err.print('x');
ic.setInstruction(new ComplexExpression(op, exprs));
ic.replace(sequBlock, ic);
ic.moveDefinitions(sequBlock, ic);
ic.replace(sequBlock);
return true;
}
return false;

@ -52,7 +52,8 @@ public class CreateForInitializer implements Transformation {
System.err.print('f');
forBlock.init = initializer;
forBlock.replace(forBlock.outer, forBlock);
forBlock.moveDefinitions(forBlock.outer, forBlock);
forBlock.replace(forBlock.outer);
return true;
}
}

@ -134,7 +134,8 @@ public class CreateIfThenElseOperator implements Transformation {
flow.removeSuccessor(condBlock.trueBlock.jump);
condBlock.trueBlock.removeJump();
pushBlock.setInstruction(cond);
pushBlock.replace(sequBlock, pushBlock);
pushBlock.moveDefinitions(sequBlock, pushBlock);
pushBlock.replace(sequBlock);
e[i+1] = cond;
}
@ -152,7 +153,8 @@ public class CreateIfThenElseOperator implements Transformation {
((InstructionBlock)ifBlock.thenBlock).
setInstruction(new ComplexExpression(iteo, e));
ifBlock.thenBlock.replace(ifBlock, ifBlock.thenBlock);
ifBlock.thenBlock.moveDefinitions(ifBlock, ifBlock.thenBlock);
ifBlock.thenBlock.replace(ifBlock);
return true;
}
@ -216,7 +218,8 @@ public class CreateIfThenElseOperator implements Transformation {
((InstructionBlock)flow.lastModified).
setInstruction(new ComplexExpression(iteo, e));
flow.lastModified.replace(flow.lastModified.outer, flow.lastModified);
flow.lastModified.moveDefinitions(flow.lastModified.outer, null);
flow.lastModified.replace(flow.lastModified.outer);
return true;
}

@ -63,7 +63,8 @@ public class CreateNewConstructor implements Transformation{
exprs[i] = exprs[i].combine(expr);
SequentialBlock subExprBlock =
(SequentialBlock) sequBlock.subBlocks[1];
subExprBlock.replace(sequBlock, subExprBlock);
subExprBlock.moveDefinitions(sequBlock, subExprBlock);
subExprBlock.replace(sequBlock);
sequBlock = subExprBlock;
((InstructionContainer)subExprBlock.subBlocks[0]).
setInstruction(exprs[i]);
@ -93,7 +94,8 @@ public class CreateNewConstructor implements Transformation{
constrCall.getField()),
exprs));
flow.lastModified.replace(sequBlock, flow.lastModified);
flow.lastModified.moveDefinitions(sequBlock, null);
flow.lastModified.replace(sequBlock);
return true;
}
}

@ -80,7 +80,8 @@ public class CreatePrePostIncExpression implements Transformation {
}
Operator ppop = new LocalPrePostFixOperator(type, op, iinc, isPost);
lastBlock.setInstruction(ppop);
lastBlock.replace(lastBlock.outer, lastBlock);
lastBlock.moveDefinitions(lastBlock.outer, lastBlock);
lastBlock.replace(lastBlock.outer);
return true;
}
@ -155,7 +156,8 @@ public class CreatePrePostIncExpression implements Transformation {
}
Operator postop = new PrePostFixOperator(type, op, store, true);
lastBlock.setInstruction(postop);
lastBlock.replace(sequBlock, lastBlock);
lastBlock.moveDefinitions(sequBlock, lastBlock);
lastBlock.replace(sequBlock);
return true;
}
}

@ -47,11 +47,12 @@ public class EmptyBlock extends StructuredBlock {
IfThenElseBlock ifBlock =
new IfThenElseBlock(((ConditionalBlock)outer).
getInstruction());
ifBlock.replace(outer, this);
ifBlock.moveDefinitions(outer, this);
ifBlock.replace(outer);
ifBlock.moveJump(outer.jump);
ifBlock.setThenBlock(this);
}
block.replace(this, null);
block.replace(this);
return block;
}

@ -53,6 +53,12 @@ public class FlowBlock {
* uses that variable, on which it is never assigned
*/
VariableSet in = new VariableSet();
/**
* The gen locals. This are the locals, to which are written
* somewhere in this flow block. This is only used for try
* catch blocks.
*/
VariableSet gen = new VariableSet();
/**
* The starting address of this flow block. This is mainly used
@ -106,15 +112,11 @@ public class FlowBlock {
this.block = block;
lastModified = block;
block.setFlowBlock(this);
block.fillInSet(in);
block.fillInGenSet(in, gen);
}
public int getNextAddr() {
if (block instanceof RawTryCatchBlock)
return ((RawTryCatchBlock)block).getTryBlock()
.jump.destination.getNextAddr();
else
return addr+length;
return addr+length;
}
/**
@ -171,7 +173,8 @@ public class FlowBlock {
prev.removeJump();
IfThenElseBlock ifBlock =
new IfThenElseBlock(cb.getInstruction());
ifBlock.replace(cb, prev);
ifBlock.moveDefinitions(cb, prev);
ifBlock.replace(cb);
ifBlock.setThenBlock(prev);
continue;
}
@ -276,7 +279,8 @@ public class FlowBlock {
= new IfThenElseBlock(instr.negate());
StructuredBlock thenBlock = sequBlock.getSubBlocks()[1];
newIfBlock.replace(sequBlock, thenBlock);
newIfBlock.moveDefinitions(sequBlock, thenBlock);
newIfBlock.replace(sequBlock);
newIfBlock.setThenBlock(thenBlock);
if (thenBlock.contains(lastModified)) {
@ -328,7 +332,7 @@ public class FlowBlock {
&& (elseBlock.getNextFlowBlock() == successor
|| elseBlock.jumpMayBeChanged())) {
ifBlock.replace(sequBlock, elseBlock);
ifBlock.replace(sequBlock);
ifBlock.setElseBlock(elseBlock);
if (elseBlock.contains(lastModified)) {
@ -433,7 +437,7 @@ public class FlowBlock {
lastModified.removeJump();
if (doWhileFalse != null) {
doWhileFalse.replace(outerMost, outerMost);
doWhileFalse.replace(outerMost);
doWhileFalse.setBody(outerMost);
doWhileFalse.jump = null;
lastModified = doWhileFalse;
@ -516,7 +520,8 @@ public class FlowBlock {
}
}
}
in.unionExact(successor.in);
this.in.unionExact(successor.in);
this.gen.unionExact(successor.gen);
if (Decompiler.debugInOut) {
System.err.println("UpdateInOut: gens : "+gens);
@ -555,7 +560,8 @@ public class FlowBlock {
}
StructuredBlock last = lastModified;
while (last.outer instanceof SequentialBlock)
while (last.outer instanceof SequentialBlock
|| last.outer instanceof TryBlock)
last = last.outer;
if (last.outer != null)
throw new AssertError("Inconsistency");
@ -703,13 +709,14 @@ public class FlowBlock {
LoopBlock forBlock =
new LoopBlock(LoopBlock.FOR, LoopBlock.TRUE);
forBlock.replace(bodyBlock, bodyBlock);
forBlock.replace(bodyBlock);
forBlock.setBody(bodyBlock);
forBlock.incr = instr;
lastModified.removeJump();
forBlock.moveDefinitions(lastModified, null);
forBlock.replaceBreakContinue(lb);
lb.bodyBlock.replace(lastModified.outer, null);
lastModified.removeJump();
lb.bodyBlock.replace(lastModified.outer);
createdForBlock = true;
}
@ -724,13 +731,14 @@ public class FlowBlock {
LoopBlock forBlock =
new LoopBlock(LoopBlock.FOR, LoopBlock.TRUE);
forBlock.replace(bodyBlock, bodyBlock);
forBlock.replace(bodyBlock);
forBlock.setBody(bodyBlock);
forBlock.incr = instr;
forBlock.moveDefinitions(lastModified, null);
lastModified.removeJump();
lastModified.outer.getSubBlocks()[0]
.replace(lastModified.outer, null);
.replace(lastModified.outer);
createdForBlock = true;
}
}
@ -746,7 +754,7 @@ public class FlowBlock {
LoopBlock whileBlock =
new LoopBlock(LoopBlock.WHILE, LoopBlock.TRUE);
whileBlock.replace(bodyBlock, bodyBlock);
whileBlock.replace(bodyBlock);
whileBlock.setBody(bodyBlock);
/* if there are further jumps to this, replace every jump with a
@ -936,13 +944,6 @@ public class FlowBlock {
boolean changed = false;
if (block instanceof RawTryCatchBlock) {
/* analyze the try and catch blocks separately
* and create a new CatchBlock afterwards.
*/
changed |= analyzeCatchBlock(start, end);
}
while (true) {
if (Decompiler.isFlowDebugging)
@ -1004,18 +1005,6 @@ public class FlowBlock {
break;
}
if (succ.block instanceof RawTryCatchBlock) {
/* analyze the try and catch blocks separately
* and create a new CatchBlock afterwards.
*/
int newStart = (succ.addr > addr)
? addr+length : start;
int newEnd = (succ.addr > addr)
? end : addr;
if (succ.analyzeCatchBlock(newStart, newEnd)) {
break;
}
}
if ((succ.addr == addr+length
|| succ.addr+succ.length == addr)
/* Only do T1 transformation if the blocks are
@ -1065,517 +1054,6 @@ public class FlowBlock {
}
}
}
public void removeJSR(FlowBlock subRoutine) {
Stack jumps = (Stack)successors.remove(subRoutine);
if (jumps == null)
return;
Enumeration enum = jumps.elements();
while (enum.hasMoreElements()) {
Jump jump = (Jump)enum.nextElement();
StructuredBlock prev = jump.prev;
prev.removeJump();
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) {
if (prev.outer.getNextFlowBlock() != null) {
/* The jsr is directly before a jump, okay. */
prev.outer.removeBlock();
continue;
}
if (prev.outer.outer instanceof SequentialBlock
&& prev.outer.outer.getSubBlocks()[0] == prev.outer) {
SequentialBlock seq = (SequentialBlock) prev.outer.outer;
if (seq.subBlocks[1] instanceof JsrBlock) {
/* The jsr is followed by a jsr, okay. */
prev.outer.removeBlock();
continue;
}
if (seq.subBlocks[1] instanceof ReturnBlock
&& !(seq.subBlocks[1] instanceof ThrowBlock)) {
/* The jsr is followed by a return, okay. */
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 {
ComplexExpression expr = (ComplexExpression)
((InstructionBlock)
ret.outer.getSubBlocks()[0]).instr;
LocalStoreOperator store =
(LocalStoreOperator) expr.getOperator();
if (store.matches((LocalLoadOperator)
ret.getInstruction())) {
ret.setInstruction(expr.
getSubExpressions()[0]);
ret.replace(ret.outer, null);
ret.used.removeElement
(store.getLocalInfo());
}
} catch(ClassCastException ex) {
/* didn't succeed */
}
}
continue;
}
}
}
/* Now we have a dangling JSR at the wrong place.
* We don't do anything, so that JSR will show up in
* the output.
*/
}
}
public void checkAndRemoveJSR(FlowBlock subRoutine) {
Enumeration keys = successors.keys();
Enumeration stacks = successors.elements();
while (keys.hasMoreElements()) {
Stack jumps = (Stack) stacks.nextElement();
if (keys.nextElement() == subRoutine)
continue;
Enumeration enum = jumps.elements();
while (enum.hasMoreElements()) {
Jump jump = (Jump)enum.nextElement();
StructuredBlock prev = jump.prev;
if (prev instanceof ThrowBlock) {
/* The jump is a throw. We have a catch-all block
* that will do the finally.
*/
continue;
}
if (prev instanceof JsrBlock) {
/* The jump is directly preceeded by a jsr.
* Everything okay.
*/
continue;
}
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) {
/* If jump is a jsr check the outer
* block instead.
*/
prev = prev.outer;
}
if ((prev instanceof ReturnBlock
|| prev instanceof JsrBlock)
&& prev.outer instanceof SequentialBlock) {
SequentialBlock seq = (SequentialBlock) prev.outer;
if (seq.subBlocks[1] == prev
&& (seq.subBlocks[0] instanceof JsrBlock)) {
/* The jump is preceeded by another jsr, okay.
*/
continue;
}
if (seq.subBlocks[0] == prev
&& seq.outer instanceof SequentialBlock
&& (seq.outer.getSubBlocks()[0] instanceof JsrBlock)) {
/* Again the jump is preceeded by another jsr, okay.
*/
continue;
}
}
/* Now we have a jump with a wrong destination.
* Complain!
*/
System.err.println("non well formed try-finally block");
}
}
removeJSR(subRoutine);
}
public boolean isMonitorExit(Expression instr, LocalInfo local) {
if (instr instanceof ComplexExpression) {
ComplexExpression expr = (ComplexExpression)instr;
if (expr.getOperator() instanceof MonitorExitOperator
&& expr.getSubExpressions()[0] instanceof LocalLoadOperator
&& (((LocalLoadOperator) expr.getSubExpressions()[0])
.getLocalInfo().getSlot() == local.getSlot())) {
return true;
}
}
return false;
}
public void checkAndRemoveMonitorExit(LocalInfo local, int end) {
FlowBlock subRoutine = null;
Enumeration stacks = successors.elements();
dest_loop:
while (stacks.hasMoreElements()) {
Enumeration enum = ((Stack) stacks.nextElement()).elements();
while (enum.hasMoreElements()) {
Jump jump = (Jump)enum.nextElement();
StructuredBlock prev = jump.prev;
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock
&& subRoutine == null) {
subRoutine = jump.destination;
subRoutine.analyzeSubRoutine(addr+length, end);
if (subRoutine.block instanceof InstructionBlock) {
Expression instr =
((InstructionBlock)subRoutine.block)
.getInstruction();
if (isMonitorExit(instr, local)) {
updateInOut(subRoutine, true,
(Stack)successors.get(subRoutine));
length += subRoutine.length;
continue dest_loop;
}
}
}
if (prev instanceof ThrowBlock) {
/* The jump is a throw. We have a catch all block
* that will do the monitorexit.
*/
continue;
}
if (prev instanceof JsrBlock) {
/* The jump is directly preceeded by a jsr.
*/
continue;
}
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) {
/* If jump is a jsr check the outer
* block instead.
*/
prev = prev.outer;
}
if ((prev instanceof JsrBlock
|| prev instanceof ReturnBlock)
&& prev.outer instanceof SequentialBlock) {
SequentialBlock seq = (SequentialBlock) prev.outer;
StructuredBlock pred = null;
if (seq.subBlocks[1] == prev)
pred = seq.subBlocks[0];
else if (seq.outer instanceof SequentialBlock)
pred = seq.outer.getSubBlocks()[0];
if (pred != null) {
if (pred instanceof JsrBlock || pred.jump != null)
/* The jump is preceeded by another jump
* or jsr and last in its block, okay.
*/
continue;
if (pred instanceof InstructionBlock) {
Expression instr =
((InstructionBlock)pred).getInstruction();
if (instr instanceof ComplexExpression
&& ((ComplexExpression)instr)
.getOperator() instanceof MonitorExitOperator
&& ((ComplexExpression)instr)
.getSubExpressions()[0]
instanceof LocalLoadOperator
&& (((LocalLoadOperator)
((ComplexExpression)instr)
.getSubExpressions()[0]).getLocalInfo()
.getSlot() == local.getSlot()))
continue;
}
}
}
if (prev instanceof InstructionBlock
&& isMonitorExit(((InstructionBlock)prev).instr, local)) {
/* This is probably the last expression in the
* synchronized block, and has the right monitor exit
* attached. Remove this block.
*/
prev.removeBlock();
continue;
}
/* Now we have a jump that is not preceded by a monitorexit.
* Complain!
*/
System.err.println("non well formed synchronized block");
}
}
if (subRoutine != null)
removeJSR(subRoutine);
}
/**
* Create a Catch- resp. FinallyBlock (maybe even SynchronizedBlock).
* The root block MUST be a RawTryCatchBlock.
* @param start the start address
* @param end and the end address of FlowBlocks, we may use.
*/
public boolean analyzeCatchBlock(int start, int end) {
if (Decompiler.debugAnalyze)
System.err.println("analyzeCatch("+start+", "+end+")");
RawTryCatchBlock rawBlock = (RawTryCatchBlock) block;
FlowBlock tryFlow = rawBlock.tryBlock.jump.destination;
FlowBlock catchFlow = rawBlock.catchBlock.jump.destination;
/* This are the only jumps in this block. Make sure they
* are disjunct, the successing code relies on that!
*/
if (tryFlow == catchFlow)
throw new AssertError("try == catch");
checkConsistent();
boolean changed = false;
while(tryFlow.analyze(addr, catchFlow.addr));
while(catchFlow.analyze(catchFlow.addr, end));
checkConsistent();
updateInOut(tryFlow, true, (Stack) successors.remove(tryFlow));
updateInOut(catchFlow, true, (Stack) successors.remove(catchFlow));
length += tryFlow.length;
length += catchFlow.length;
if (rawBlock.type != null) {
/* simple try catch block:
*
* try-header
* |- first instruction
* | ...
* | last instruction
* |- optional jump (last+1)
* | ...
* `- catch block
*/
CatchBlock newBlock = new CatchBlock(rawBlock.type);
newBlock.replace(rawBlock, rawBlock);
newBlock.moveJump(rawBlock.jump);
newBlock.setTryBlock(tryFlow.block);
mergeSuccessors(tryFlow);
newBlock.setCatchBlock(catchFlow.block);
mergeSuccessors(catchFlow);
lastModified = newBlock;
changed = true;
} else if (catchFlow.block instanceof SequentialBlock
&& catchFlow.block.getSubBlocks()[0]
instanceof InstructionBlock) {
SequentialBlock catchBlock = (SequentialBlock) catchFlow.block;
int type = 0;
Expression instr =
((InstructionBlock)catchBlock.subBlocks[0]).getInstruction();
if (instr instanceof ComplexExpression
&& ((ComplexExpression)instr).getOperator()
instanceof MonitorExitOperator
&& ((ComplexExpression)instr).getSubExpressions()[0]
instanceof LocalLoadOperator
&& catchBlock.subBlocks[1] instanceof ThrowBlock
&& ((ThrowBlock)catchBlock.subBlocks[1]).instr
instanceof NopOperator) {
/* This is a synchronized block:
*
* local_x = monitor object; // later
* monitorenter local_x // later
* try-header any
* |- syncronized block
* | ...
* | every jump to outside is preceded by jsr monexit ---,
* | ... |
* |- monitorexit local_x |
* | jump after this block (without jsr monexit) |
* `- catch any |
* local_n = stack |
* monitorexit local_x |
* throw local_n |
* monexit: <-----------------------------------------------'
* 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.
*/
catchBlock.subBlocks[1]
.jump.destination.predecessors.removeElement(catchFlow);
ComplexExpression monexit = (ComplexExpression)
((InstructionBlock) catchBlock.subBlocks[0]).instr;
LocalInfo local =
((LocalLoadOperator)monexit.getSubExpressions()[0])
.getLocalInfo();
length -= tryFlow.length;
tryFlow.checkAndRemoveMonitorExit(local, end);
length += tryFlow.length;
SynchronizedBlock syncBlock = new SynchronizedBlock(local);
syncBlock.replace(rawBlock, rawBlock);
syncBlock.moveJump(rawBlock.jump);
syncBlock.setBodyBlock(tryFlow.block);
mergeSuccessors(tryFlow);
lastModified = syncBlock;
changed = true;
} else if (catchBlock.subBlocks[1] instanceof SequentialBlock
&& catchBlock.subBlocks[1].getSubBlocks()[0]
instanceof JsrBlock
&& instr instanceof LocalStoreOperator
&& catchBlock.subBlocks[1].getSubBlocks()[1]
instanceof ThrowBlock
&& ((ThrowBlock)catchBlock.subBlocks[1]
.getSubBlocks()[1]).instr
instanceof LocalLoadOperator
&& ((LocalStoreOperator) instr)
.matches((LocalLoadOperator)
((ThrowBlock)catchBlock.subBlocks[1]
.getSubBlocks()[1]).instr)) {
/* Wow that was complicated :-)
* But now we know that the catch block looks
* exactly like an try finally block:
*
* try-header any
* |
* |- first instruction
* | ...
* | every jump to outside is preceded by jsr finally
* | ...
* | jsr finally -----------------,
* |- jump after finally |
* `- catch any |
* local_n = stack v
* jsr finally ---------------->|
* throw local_n; |
* finally: <-----------------------'
* astore_n
* ...
* return_n
*/
FlowBlock subRoutine =
((JsrBlock)catchBlock.subBlocks[1].getSubBlocks()[0])
.innerBlock.jump.destination;
/* Now remove the two jumps of the catch block
* so that we can forget about them.
* This are the jsr and the throw.
*/
catchBlock.subBlocks[1].getSubBlocks()[0].getSubBlocks()[0]
.jump.destination.predecessors.removeElement(catchFlow);
catchBlock.subBlocks[1].getSubBlocks()[1]
.jump.destination.predecessors.removeElement(catchFlow);
subRoutine.analyzeSubRoutine(addr+length, end);
Stack jumps = (Stack)successors.get(subRoutine);
if (jumps != null)
updateInOut(subRoutine, true, jumps);
if (subRoutine.successors.size() != 0)
throw new AssertError("Jump inside subroutine");
length += subRoutine.length;
tryFlow.checkAndRemoveJSR(subRoutine);
CatchFinallyBlock newBlock = new CatchFinallyBlock();
newBlock.replace(rawBlock, rawBlock);
newBlock.setTryBlock(tryFlow.block);
mergeSuccessors(tryFlow);
newBlock.setFinallyBlock(subRoutine.block);
mergeSuccessors(subRoutine);
newBlock.moveJump(rawBlock.jump);
lastModified = newBlock;
changed = true;
}
} else if (catchFlow.block instanceof InstructionBlock
&& ((InstructionBlock) catchFlow.block).getInstruction()
instanceof PopOperator
&& ((PopOperator) ((InstructionBlock) catchFlow.block)
.getInstruction()).getCount() == 1) {
/* This is a special try/finally-block, where
* the finally block ends with a break, return or
* similar.
*/
FlowBlock succ = catchFlow.block.jump.destination;
Stack jumps = (Stack) tryFlow.successors.remove(succ);
if (tryFlow.successors.size() > 0) {
/* Only do the rest if tryFlow has no other exit point,
* undo the previous remove.
*/
tryFlow.successors.put(succ,jumps);
} else {
succ.predecessors.removeElement(tryFlow);
/* Handle the jumps in the tryFlow. Note that
* we call updateInOut on ourself, don't change it.
*/
updateInOut(succ, true, jumps);
tryFlow.optimizeJumps(jumps, succ);
tryFlow.resolveRemaining(jumps);
CatchFinallyBlock newBlock = new CatchFinallyBlock();
newBlock.replace(rawBlock, rawBlock);
newBlock.setTryBlock(tryFlow.block);
/* try block has no successors */
if (succ.predecessors.size() == 1) {
while (succ.analyze(addr+length, end));
length += succ.length;
successors.remove(succ);
newBlock.setFinallyBlock(succ.block);
mergeSuccessors(succ);
} else {
/* The finally block is empty, put the jump back
* into the finally block.
*/
newBlock.setFinallyBlock
(new EmptyBlock(catchFlow.block.jump));
mergeSuccessors(catchFlow);
}
lastModified = newBlock;
changed = true;
}
}
checkConsistent();
if (Decompiler.debugAnalyze)
System.err.println("analyzeCatch("+start+", "+end+") "
+(changed?"succeeded":"failed")
+"; "+addr+","+(addr+length));
return changed;
}
public void analyzeSubRoutine(int start, int end) {
analyze(start, end);
/* throws ClassCastException if something isn't as exspected. */
SequentialBlock sequBlock = (SequentialBlock) block;
LocalStoreOperator store = (LocalStoreOperator)
((InstructionBlock)sequBlock.subBlocks[0]).instr.getOperator();
while (sequBlock.subBlocks[1] instanceof SequentialBlock)
sequBlock = (SequentialBlock) sequBlock.subBlocks[1];
if (! ((RetBlock)sequBlock.subBlocks[1]).local
.equals(store.getLocalInfo()))
throw new AssertError("Ret doesn't match");
if (sequBlock.outer == null) {
new EmptyBlock().replace(sequBlock, sequBlock);
} else {
sequBlock.subBlocks[0].replace(sequBlock, sequBlock);
block.getSubBlocks()[1].replace(block, block);
}
}
/**
* The switch analyzation. This calls doSwitchT1 and doT2 on apropriate
@ -1646,7 +1124,7 @@ public class FlowBlock {
if (lastFlow != null) {
lastFlow.block.replace
(switchBlock.caseBlocks[last].subBlock, null);
(switchBlock.caseBlocks[last].subBlock);
mergeSuccessors(lastFlow);
}
@ -1667,7 +1145,7 @@ public class FlowBlock {
}
if (lastFlow != null) {
lastFlow.block.replace
(switchBlock.caseBlocks[last].subBlock, null);
(switchBlock.caseBlocks[last].subBlock);
mergeSuccessors(lastFlow);
}
checkConsistent();
@ -1679,10 +1157,6 @@ public class FlowBlock {
* the successors map.
*/
public void resolveJumps(FlowBlock[] instr) {
if (block instanceof RawTryCatchBlock) {
((RawTryCatchBlock)block).getTryBlock()
.jump.destination.resolveJumps(instr);
}
Vector allJumps = new Vector();
block.fillSuccessors(allJumps);
Enumeration enum = allJumps.elements();
@ -1739,8 +1213,7 @@ public class FlowBlock {
public void dumpSource(TabbedPrintWriter writer)
throws java.io.IOException
{
if (predecessors.size() != 1 ||
predecessors.elementAt(0) != null) {
if (predecessors.size() != 0) {
writer.untab();
writer.println(label+":");
writer.tab();

@ -46,12 +46,13 @@ public abstract class InstructionContainer extends StructuredBlock {
* Fill all in variables into the given VariableSet.
* @param in The VariableSet, the in variables should be stored to.
*/
public void fillInSet(VariableSet in) {
public void fillInGenSet(VariableSet in, VariableSet gen) {
if (instr instanceof LocalVarOperator) {
LocalVarOperator varOp = (LocalVarOperator) instr;
if (varOp.isRead()) {
in.addElement(varOp.getLocalInfo());
}
gen.addElement(varOp.getLocalInfo());
}
}

@ -237,7 +237,7 @@ public class LoopBlock extends StructuredBlock implements BreakableBlock {
BreakBlock breakblk = (BreakBlock) subs[i];
if (breakblk.breaksBlock == block) {
new ContinueBlock(this, breakblk.label != null)
.replace(breakblk, null);
.replace(breakblk);
}
}
todo.push(subs[i]);

@ -49,7 +49,7 @@ public class RemoveEmpty implements Transformation {
instr.setType(nopInstr.getType());
ic.setInstruction(instr);
ic.replace(ic.outer, ic);
ic.replace(ic.outer);
return true;
}
return false;
@ -62,7 +62,7 @@ public class RemoveEmpty implements Transformation {
lastBlock.outer.getSubBlocks()[1] == lastBlock) {
StructuredBlock block = lastBlock.outer.getSubBlocks()[0];
block.replace(block.outer, block);
block.replace(block.outer);
if (lastBlock.jump != null)
block.moveJump(lastBlock.jump);
flow.lastModified = block;
@ -72,7 +72,7 @@ public class RemoveEmpty implements Transformation {
lastBlock.outer.getSubBlocks()[0] instanceof EmptyBlock &&
lastBlock.outer.getSubBlocks()[0].jump == null) {
lastBlock.replace(lastBlock.outer, lastBlock);
lastBlock.replace(lastBlock.outer);
flow.lastModified = lastBlock;
return true;
}

@ -41,8 +41,9 @@ public class RetBlock extends StructuredBlock {
* Fill all in variables into the given VariableSet.
* @param in The VariableSet, the in variables should be stored to.
*/
public void fillInSet(VariableSet in) {
public void fillInGenSet(VariableSet in, VariableSet gen) {
in.addElement(local);
gen.addElement(local);
}
public void dumpInstruction(jode.TabbedPrintWriter writer)

@ -78,9 +78,16 @@ public abstract class StructuredBlock {
* block in a flow block, outer is null. */
StructuredBlock outer;
// /**
// * The surrounding non sequential block. This is the same as if
// * you would repeatedly get outer until you reach a non sequential
// * block. This is field is only valid, if the outer block is a
// * sequential block.
// */
// StructuredBlock realOuter;
/**
* The flow block in which this structured block lies.
*/
* The flow block in which this structured block lies. */
FlowBlock flowBlock;
/**
@ -231,8 +238,7 @@ public abstract class StructuredBlock {
* @param sub The uppermost sub block of structured block,
* that will be moved to this block (may be this).
*/
public void replace(StructuredBlock sb, StructuredBlock sub) {
moveDefinitions(sb, sub);
public void replace(StructuredBlock sb) {
outer = sb.outer;
setFlowBlock(sb.flowBlock);
if (outer != null) {
@ -277,7 +283,7 @@ public abstract class StructuredBlock {
*/
public StructuredBlock appendBlock(StructuredBlock block) {
SequentialBlock sequBlock = new SequentialBlock();
sequBlock.replace(this, this);
sequBlock.replace(this);
sequBlock.setFirst(this);
sequBlock.setSecond(block);
return sequBlock;
@ -292,15 +298,15 @@ public abstract class StructuredBlock {
if (outer.getSubBlocks()[1] == this) {
if (jump != null)
outer.getSubBlocks()[0].moveJump(jump);
outer.getSubBlocks()[0].replace(outer, null);
outer.getSubBlocks()[0].replace(outer);
} else
outer.getSubBlocks()[1].replace(outer, null);
outer.getSubBlocks()[1].replace(outer);
return;
}
EmptyBlock eb = new EmptyBlock();
eb.moveJump(jump);
eb.replace(this, null);
eb.replace(this);
}
/**
@ -398,7 +404,7 @@ public abstract class StructuredBlock {
* Fill all in variables into the given VariableSet.
* @param in The VariableSet, the in variables should be stored to.
*/
public void fillInSet(VariableSet in) {
public void fillInGenSet(VariableSet in, VariableSet gen) {
/* overwritten by InstructionContainer */
}

@ -0,0 +1,812 @@
/* TransformExceptionHandlers Copyright (C) 1997-1998 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
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package jode.flow;
import jode.AssertError;
import jode.Type;
import jode.LocalInfo;
import jode.ComplexExpression;
import jode.LocalStoreOperator;
import jode.NopOperator;
import jode.MonitorExitOperator;
import jode.LocalLoadOperator;
import jode.Expression;
import jode.PopOperator;
import java.util.Enumeration;
import java.util.Stack;
/**
*
* @author Jochen Hoenicke
*/
public class TransformExceptionHandlers {
FlowBlock[] flows;
int count;
int[] startPCs = new int[4];
int[] endPCs = new int[4];
int[] handlerPCs = new int[4];
Type[] types = new Type[4];
public TransformExceptionHandlers(FlowBlock[] flowBlocks) {
flows = flowBlocks;
count = 0;
}
/**
* Add an exception Handler.
* @param start The start address of the exception range.
* @param end The end address of the exception range + 1.
* @param handler The address of the handler.
* @param type The type of the exception, null for ALL.
*/
public void addHandler(int start, int end, int handler, Type type) {
int offset = 0;
/* First sort by start offsets, highest address first...*/
while (offset < count && start < startPCs[offset])
offset++;
/* ...Second sort by end offsets, lowest address first...
* this will move the innermost blocks to the beginning. */
while (offset < count && start == startPCs[offset]
&& end > endPCs[offset])
offset++;
/* ...Last sort by handler offsets, lowest first */
while (offset < count && start == startPCs[offset]
&& end == endPCs[offset] && handler > handlerPCs[offset])
offset++;
if (count++ >= startPCs.length) {
/* We grow the arrays by 50 % */
int newSize = startPCs.length * 3 / 2;
int[] newStartPCs = new int[newSize];
int[] newEndPCs = new int[newSize];
int[] newHandlerPCs = new int[newSize];
Type[] newTypes = new Type[newSize];
System.arraycopy(startPCs, 0, newStartPCs, 0, offset);
System.arraycopy(endPCs, 0, newEndPCs, 0, offset);
System.arraycopy(handlerPCs, 0, newHandlerPCs, 0, offset);
System.arraycopy(types, 0, newTypes, 0, offset);
if (offset+1 < count) {
System.arraycopy(startPCs, offset, newStartPCs, offset+1,
count-offset-1);
System.arraycopy(endPCs, offset, newEndPCs, offset+1,
count-offset-1);
System.arraycopy(handlerPCs, offset, newHandlerPCs, offset+1,
count-offset-1);
System.arraycopy(types, offset, newTypes, offset+1,
count-offset-1);
}
startPCs = newStartPCs;
endPCs = newEndPCs;
handlerPCs = newHandlerPCs;
types = newTypes;
} else if (offset+1 < count) {
/* Move the tailing data one place below
*/
System.arraycopy(startPCs, offset, startPCs, offset+1,
count-offset-1);
System.arraycopy(endPCs, offset, endPCs, offset+1,
count-offset-1);
System.arraycopy(handlerPCs, offset, handlerPCs, offset+1,
count-offset-1);
System.arraycopy(types, offset, types, offset+1,
count-offset-1);
}
/* Insert the new handler */
startPCs[offset] = start;
endPCs[offset] = end;
handlerPCs[offset] = handler;
types[offset] = type;
}
/**
* Updates the in/out-Vectors of the structured block of the
* successing flow block for a try catch block. The main difference
* to updateInOut in FlowBlock is, that this function works, as if
* every instruction would have a jump. This is because every
* instruction can throw an exception and thus enter the catch block.
*
* @param successor The flow block which is unified with this flow
* block.
* @return The variables that must be defined in this block.
*/
static void updateInOutCatch (FlowBlock tryFlow, FlowBlock catchFlow) {
VariableSet gens = ((TryBlock)tryFlow.block).gen;
/* Merge the locals used in the catch block with those written
* by the try block
*/
catchFlow.in.merge(gens);
/* The gen/kill sets must be updated for every jump
* in the catch block */
Enumeration stacks = catchFlow.successors.elements();
while (stacks.hasMoreElements()) {
Enumeration enum = ((Stack) stacks.nextElement()).elements();
while (enum.hasMoreElements()) {
Jump jump = (Jump) enum.nextElement();
if (jump != null) {
jump.gen.mergeGenKill(gens, jump.kill);
}
}
}
tryFlow.in.unionExact(catchFlow.in);
tryFlow.gen.unionExact(catchFlow.gen);
if (jode.Decompiler.debugInOut) {
System.err.println("UpdateInOutCatch: gens : "+gens);
System.err.println(" s.in : "+catchFlow.in);
System.err.println(" in : "+tryFlow.in);
}
}
/* simple try catch block:
*
* try-header
* |- first instruction
* | ...
* | last instruction
* |- optional jump (last+1)
* | ...
* `- catch block
*/
static int serialno=0;
static void analyzeCatchBlock(Type type,
FlowBlock tryFlow, FlowBlock catchFlow) {
StructuredBlock catchBlock = catchFlow.block;
LocalInfo local = null;
StructuredBlock firstInstr = (catchBlock instanceof SequentialBlock)
? catchBlock.getSubBlocks()[0] : catchBlock;
if (firstInstr instanceof InstructionBlock) {
Expression instr =
((InstructionBlock) firstInstr).getInstruction();
if (instr instanceof PopOperator
&& ((PopOperator) instr).getCount() == 1) {
/* The exception is ignored. Create a dummy local for it */
local = new LocalInfo(-1);
local.setName("exception_"+(serialno++)+"_");
firstInstr.removeBlock();
} else if (instr instanceof jode.LocalStoreOperator) {
/* The exception is stored in a local variable */
local =
((jode.LocalStoreOperator) instr).getLocalInfo();
firstInstr.removeBlock();
}
}
if (local == null) {
local = new LocalInfo(-1);
local.setName("ERROR!!!");
}
local.setType(type);
CatchBlock newBlock = new CatchBlock(type, local);
((TryBlock)tryFlow.block).addCatchBlock(newBlock);
newBlock.setCatchBlock(catchFlow.block);
tryFlow.mergeSuccessors(catchFlow);
tryFlow.length += catchFlow.length;
}
/* 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.
*/
boolean transformSubRoutine(FlowBlock subRoutine) {
try {
SequentialBlock sequBlock = (SequentialBlock) subRoutine.block;
LocalStoreOperator store = (LocalStoreOperator)
((InstructionBlock)sequBlock.subBlocks[0]).instr.getOperator();
while (sequBlock.subBlocks[1] instanceof SequentialBlock)
sequBlock = (SequentialBlock) sequBlock.subBlocks[1];
RetBlock retBlock = (RetBlock)sequBlock.subBlocks[1];
if (! retBlock.local.equals(store.getLocalInfo()))
/* Ret doesn't match */
return false;
subRoutine.block.getSubBlocks()[0].removeBlock();
retBlock.removeBlock();
return true;
} catch (ClassCastException ex) {
return false;
}
}
/**
* Remove the JSR's jumping to the specified subRoutine. It
* is checked if the next block is a leaving instruction, and
* otherwise the JsrBlock is not removed (to give the user a
* hint that something went wrong). This will also remove the
* local javac generates for returns.
* @param tryFlow the FlowBLock of the try block.
* @param subRoutine the FlowBlock of the sub routine.
*/
private void removeJSR(FlowBlock tryFlow, FlowBlock subRoutine) {
Stack jumps = (Stack)tryFlow.successors.remove(subRoutine);
if (jumps == null)
return;
Enumeration enum = jumps.elements();
while (enum.hasMoreElements()) {
Jump jump = (Jump)enum.nextElement();
StructuredBlock prev = jump.prev;
prev.removeJump();
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) {
if (prev.outer.getNextFlowBlock() != null) {
/* The jsr is directly before a jump, okay. */
prev.outer.removeBlock();
continue;
}
if (prev.outer.outer instanceof SequentialBlock
&& prev.outer.outer.getSubBlocks()[0] == prev.outer) {
SequentialBlock seq = (SequentialBlock) prev.outer.outer;
if (seq.subBlocks[1] instanceof JsrBlock) {
/* The jsr is followed by a jsr, okay. */
prev.outer.removeBlock();
continue;
}
if (seq.subBlocks[1] instanceof ReturnBlock
&& !(seq.subBlocks[1] instanceof ThrowBlock)) {
/* The jsr is followed by a return, okay. */
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 {
ComplexExpression expr = (ComplexExpression)
((InstructionBlock)
ret.outer.getSubBlocks()[0]).instr;
LocalStoreOperator store =
(LocalStoreOperator) expr.getOperator();
if (store.matches((LocalLoadOperator)
ret.getInstruction())) {
ret.setInstruction(expr.
getSubExpressions()[0]);
ret.replace(ret.outer);
ret.used.removeElement
(store.getLocalInfo());
}
} catch(ClassCastException ex) {
/* didn't succeed */
}
}
continue;
}
}
}
/* Now we have a dangling JSR at the wrong place.
* We don't do anything, so that JSR will show up in
* the output.
*/
}
}
public void checkAndRemoveJSR(FlowBlock tryFlow, FlowBlock subRoutine) {
Enumeration keys = tryFlow.successors.keys();
Enumeration stacks = tryFlow.successors.elements();
while (keys.hasMoreElements()) {
Stack jumps = (Stack) stacks.nextElement();
if (keys.nextElement() == subRoutine)
continue;
Enumeration enum = jumps.elements();
while (enum.hasMoreElements()) {
Jump jump = (Jump)enum.nextElement();
StructuredBlock prev = jump.prev;
if (prev instanceof ThrowBlock) {
/* The jump is a throw. We have a catch-all block
* that will do the finally.
*/
continue;
}
if (prev instanceof JsrBlock) {
/* The jump is directly preceeded by a jsr.
* Everything okay.
*/
continue;
}
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) {
/* If jump is a jsr check the outer
* block instead.
*/
prev = prev.outer;
}
if ((prev instanceof ReturnBlock
|| prev instanceof JsrBlock)
&& prev.outer instanceof SequentialBlock) {
SequentialBlock seq = (SequentialBlock) prev.outer;
if (seq.subBlocks[1] == prev
&& (seq.subBlocks[0] instanceof JsrBlock)) {
/* The jump is preceeded by another jsr, okay.
*/
continue;
}
if (seq.subBlocks[0] == prev
&& seq.outer instanceof SequentialBlock
&& (seq.outer.getSubBlocks()[0] instanceof JsrBlock)) {
/* Again the jump is preceeded by another jsr, okay.
*/
continue;
}
}
/* Now we have a jump with a wrong destination.
* Complain!
*/
System.err.println("non well formed try-finally block");
}
}
removeJSR(tryFlow, subRoutine);
}
static boolean isMonitorExit(Expression instr, LocalInfo local) {
if (instr instanceof ComplexExpression) {
ComplexExpression expr = (ComplexExpression)instr;
if (expr.getOperator() instanceof MonitorExitOperator
&& expr.getSubExpressions()[0] instanceof LocalLoadOperator
&& (((LocalLoadOperator) expr.getSubExpressions()[0])
.getLocalInfo().getSlot() == local.getSlot())) {
return true;
}
}
return false;
}
public void checkAndRemoveMonitorExit(FlowBlock tryFlow, LocalInfo local,
int startMonExit, int endMonExit) {
FlowBlock subRoutine = null;
Enumeration stacks = tryFlow.successors.elements();
dest_loop:
while (stacks.hasMoreElements()) {
Enumeration enum = ((Stack) stacks.nextElement()).elements();
while (enum.hasMoreElements()) {
Jump jump = (Jump)enum.nextElement();
StructuredBlock prev = jump.prev;
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock
&& subRoutine == null) {
subRoutine = jump.destination;
subRoutine.analyze(startMonExit, endMonExit);
transformSubRoutine(subRoutine);
if (subRoutine.block instanceof InstructionBlock) {
Expression instr =
((InstructionBlock)subRoutine.block)
.getInstruction();
if (isMonitorExit(instr, local)) {
tryFlow.length += subRoutine.length;
continue dest_loop;
}
}
}
if (prev instanceof ThrowBlock) {
/* The jump is a throw. We have a catch all block
* that will do the monitorexit.
*/
continue;
}
if (prev instanceof JsrBlock) {
/* The jump is directly preceeded by a jsr.
*/
continue;
}
if (prev instanceof EmptyBlock
&& prev.outer instanceof JsrBlock) {
/* If jump is a jsr check the outer
* block instead.
*/
prev = prev.outer;
}
if ((prev instanceof JsrBlock
|| prev instanceof ReturnBlock)
&& prev.outer instanceof SequentialBlock) {
SequentialBlock seq = (SequentialBlock) prev.outer;
StructuredBlock pred = null;
if (seq.subBlocks[1] == prev)
pred = seq.subBlocks[0];
else if (seq.outer instanceof SequentialBlock)
pred = seq.outer.getSubBlocks()[0];
if (pred != null) {
if (pred instanceof JsrBlock || pred.jump != null)
/* The jump is preceeded by another jump
* or jsr and last in its block, okay.
*/
continue;
if (pred instanceof InstructionBlock) {
Expression instr =
((InstructionBlock)pred).getInstruction();
if (instr instanceof ComplexExpression
&& ((ComplexExpression)instr)
.getOperator() instanceof MonitorExitOperator
&& ((ComplexExpression)instr)
.getSubExpressions()[0]
instanceof LocalLoadOperator
&& (((LocalLoadOperator)
((ComplexExpression)instr)
.getSubExpressions()[0]).getLocalInfo()
.getSlot() == local.getSlot()))
continue;
}
}
}
if (prev instanceof InstructionBlock
&& isMonitorExit(((InstructionBlock)prev).instr, local)) {
/* This is probably the last expression in the
* synchronized block, and has the right monitor exit
* attached. Remove this block.
*/
prev.removeBlock();
continue;
}
/* Now we have a jump that is not preceded by a monitorexit.
* Complain!
*/
System.err.println("non well formed synchronized block");
}
}
if (subRoutine != null)
removeJSR(tryFlow, subRoutine);
}
private boolean analyzeSynchronized(FlowBlock tryFlow,
FlowBlock catchFlow,
int endHandler) {
if (!(catchFlow.block instanceof SequentialBlock
&& catchFlow.block.getSubBlocks()[0]
instanceof InstructionBlock))
return false;
SequentialBlock catchBlock = (SequentialBlock) catchFlow.block;
Expression instr =
((InstructionBlock)catchBlock.subBlocks[0]).getInstruction();
if (instr instanceof ComplexExpression
&& ((ComplexExpression)instr).getOperator()
instanceof MonitorExitOperator
&& ((ComplexExpression)instr).getSubExpressions()[0]
instanceof LocalLoadOperator
&& catchBlock.subBlocks[1] instanceof ThrowBlock
&& ((ThrowBlock)catchBlock.subBlocks[1]).instr
instanceof NopOperator) {
/* This is a synchronized block:
*
* local_x = monitor object; // later
* monitorenter local_x // later
* tryFlow:
* |- syncronized block
* | ...
* | every jump to outside is preceded by jsr subroutine-,
* | ... |
* |- monitorexit local_x |
* ` jump after this block (without jsr monexit) |
* |
* catchFlow: |
* local_n = stack |
* monitorexit local_x |
* throw local_n |
* oprtional 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.
*/
catchBlock.subBlocks[1]
.jump.destination.predecessors.removeElement(catchFlow);
ComplexExpression monexit = (ComplexExpression)
((InstructionBlock) catchBlock.subBlocks[0]).instr;
LocalInfo local =
((LocalLoadOperator)monexit.getSubExpressions()[0])
.getLocalInfo();
tryFlow.length += catchFlow.length;
checkAndRemoveMonitorExit
(tryFlow, local, catchFlow.addr+catchFlow.length, endHandler);
SynchronizedBlock syncBlock = new SynchronizedBlock(local);
TryBlock tryBlock = (TryBlock) tryFlow.block;
syncBlock.replace(tryBlock);
syncBlock.moveJump(tryBlock.jump);
syncBlock.setBodyBlock(tryBlock.subBlocks.length == 1
? tryBlock.subBlocks[0] : tryBlock);
tryFlow.lastModified = syncBlock;
return true;
}
return false;
}
private boolean analyzeFinally(FlowBlock tryFlow, FlowBlock catchFlow,
int end) {
if (!(catchFlow.block instanceof SequentialBlock
&& catchFlow.block.getSubBlocks()[0]
instanceof InstructionBlock))
return false;
SequentialBlock catchBlock = (SequentialBlock) catchFlow.block;
Expression instr =
((InstructionBlock)catchBlock.subBlocks[0]).getInstruction();
if (catchBlock.subBlocks[1] instanceof SequentialBlock
&& catchBlock.subBlocks[1].getSubBlocks()[0]
instanceof JsrBlock
&& instr instanceof LocalStoreOperator
&& catchBlock.subBlocks[1].getSubBlocks()[1]
instanceof ThrowBlock
&& ((ThrowBlock)catchBlock.subBlocks[1]
.getSubBlocks()[1]).instr
instanceof LocalLoadOperator
&& ((LocalStoreOperator) instr)
.matches((LocalLoadOperator)
((ThrowBlock)catchBlock.subBlocks[1]
.getSubBlocks()[1]).instr)) {
/* Wow that was complicated :-)
* But now we know that the catch block looks
* exactly like an try finally block:
*
* tryFlow:
* |- first instruction
* | ...
* | every jump to outside is preceded by jsr finally
* | ...
* | jsr finally -----------------,
* `- jump after finally |
* |
* catchFlow: (already checked) |
* local_n = stack v
* jsr finally ---------------->|
* throw local_n; |
* finally: <-----------------------'
* astore_n
* ...
* return_n
*/
FlowBlock subRoutine =
((JsrBlock)catchBlock.subBlocks[1].getSubBlocks()[0])
.innerBlock.jump.destination;
/* Now remove the two jumps of the catch block
* so that we can forget about them.
* This are the jsr and the throw.
*/
catchBlock.subBlocks[1].getSubBlocks()[0].getSubBlocks()[0]
.jump.destination.predecessors.removeElement(catchFlow);
catchBlock.subBlocks[1].getSubBlocks()[1]
.jump.destination.predecessors.removeElement(catchFlow);
subRoutine.analyze(catchFlow.addr+catchFlow.length, end);
if (!transformSubRoutine(subRoutine))
return false;
Stack jumps = (Stack)tryFlow.successors.get(subRoutine);
if (jumps != null)
/* jumps may be null, if tryFlow ends with a throw */
tryFlow.updateInOut(subRoutine, true, jumps);
tryFlow.length += catchFlow.length;
checkAndRemoveJSR(tryFlow, subRoutine);
TryBlock tryBlock = (TryBlock)tryFlow.block;
if (tryBlock.getSubBlocks()[0] instanceof TryBlock) {
/* remove the nested tryBlock */
TryBlock innerTry = (TryBlock)tryBlock.getSubBlocks()[0];
innerTry.gen = tryBlock.gen;
innerTry.replace(tryBlock);
tryBlock = innerTry;
tryFlow.lastModified = innerTry;
}
FinallyBlock newBlock = new FinallyBlock();
newBlock.setCatchBlock(subRoutine.block);
tryBlock.addCatchBlock(newBlock);
tryFlow.mergeSuccessors(subRoutine);
tryFlow.length += subRoutine.length;
return true;
}
return false;
}
private boolean analyzeSpecialFinally(FlowBlock tryFlow,
FlowBlock catchFlow, int end) {
StructuredBlock firstInstr =
catchFlow.block instanceof SequentialBlock
? catchFlow.block.getSubBlocks()[0]: catchFlow.block;
if (firstInstr instanceof InstructionBlock
&& ((InstructionBlock) firstInstr).getInstruction()
instanceof PopOperator
&& ((PopOperator) ((InstructionBlock) firstInstr)
.getInstruction()).getCount() == 1) {
/* This is a special try/finally-block, where
* the finally block ends with a break, return or
* similar.
*/
FlowBlock succ = (firstInstr.jump != null) ?
firstInstr.jump.destination : null;
boolean hasExit = false;
Enumeration keys = tryFlow.successors.keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if (key == succ)
continue;
if (key != tryFlow.END_OF_METHOD) {
/* There is another exit in the try block, bad */
return false;
}
Enumeration enum =
((Stack)tryFlow.successors.get(key)).elements();
while (enum.hasMoreElements()) {
Jump throwJump= (Jump)enum.nextElement();
if (!(throwJump.prev instanceof ThrowBlock)) {
/* There is a return exit in the try block */
return false;
}
}
}
/* remove the pop now */
firstInstr.removeBlock();
if (succ != null) {
Stack jumps = (Stack) tryFlow.successors.remove(succ);
succ.predecessors.removeElement(tryFlow);
/* Handle the jumps in the tryFlow.
*/
tryFlow.updateInOut(succ, true, jumps);
tryFlow.optimizeJumps(jumps, succ);
tryFlow.resolveRemaining(jumps);
}
tryFlow.length += catchFlow.length;
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);
/* try block has no successors */
if (succ != null && succ.predecessors.size() == 1) {
while (succ.analyze(catchFlow.addr+catchFlow.length, end));
tryFlow.length += succ.length;
tryFlow.successors.remove(succ);
newBlock.setCatchBlock(succ.block);
tryFlow.mergeSuccessors(succ);
} else {
/* Put the catchBlock in instaed.
*/
newBlock.setCatchBlock(catchFlow.block);
tryFlow.mergeSuccessors(catchFlow);
}
return true;
}
return false;
}
/**
* Analyzes all exception handlers to try/catch/finally or
* synchronized blocks.
*/
public void analyze() {
/* Check if try/catch ranges are okay. The following succeeds
* for all classes generated by the sun java compiler, but hand
* optimized classes will fail this.
*/
for (int i=0; i<count; i++) {
int start = startPCs[i];
int end = endPCs[i];
int handler = handlerPCs[i];
if (start >= end || handler < end)
throw new AssertError("ExceptionHandler order failed: not "
+ start + " < " + end + " <= " +
handler);
if (i == 0
|| startPCs[i-1] != start || endPCs[i-1] != end) {
/* The last handler does catch another range. */
if ( /*handler > end + 1 || */
(i > 0 && end > startPCs[i-1] && end < endPCs[i-1]))
throw new AssertError("ExceptionHandler"
+ " at wrong position: "
+ "end: "+end + " handler: "+handler
+ (i>0 ? " last: ("+startPCs[i-1]
+", "+endPCs[i-1]+", "
+handlerPCs[i-1]+")"
:""));
}
}
for (int i=0; i<count; i++) {
int endHandler = (i< count-1 && endPCs[i+1] > handlerPCs[i])
? endPCs[i+1]
: Integer.MAX_VALUE;
if (jode.Decompiler.debugAnalyze)
System.err.println("analyzeCatch(" + startPCs[i] + ", "
+ endPCs[i] + ", " +handlerPCs[i] + ")");
FlowBlock tryFlow = flows[startPCs[i]];
while (tryFlow.analyze(startPCs[i], handlerPCs[i]));
if (i == 0
|| startPCs[i-1] != startPCs[i] || endPCs[i-1] != endPCs[i]) {
/* The last handler does catch another range.
* Create a new try block.
*/
TryBlock tryBlock = new TryBlock(tryFlow);
} else if (! (tryFlow.block instanceof TryBlock))
throw new AssertError("no TryBlock");
FlowBlock catchFlow = flows[handlerPCs[i]];
while (catchFlow.analyze(handlerPCs[i], endHandler));
if (!catchFlow.predecessors.isEmpty())
throw new AssertError("Handler has a predecessors");
updateInOutCatch(tryFlow, catchFlow);
if (types[i] != null) {
analyzeCatchBlock(types[i], tryFlow, catchFlow);
} else if (!analyzeSynchronized(tryFlow, catchFlow, endHandler)
&& ! analyzeFinally(tryFlow, catchFlow, endHandler)
&& ! analyzeSpecialFinally(tryFlow, catchFlow,
endHandler))
analyzeCatchBlock(jode.Type.tObject, tryFlow, catchFlow);
tryFlow.checkConsistent();
if (jode.Decompiler.debugAnalyze)
System.err.println("analyzeCatch(" + tryFlow.addr + ", "
+ (tryFlow.addr + tryFlow.length) +
") done.");
}
}
}

@ -0,0 +1,103 @@
/* jode.flow.TryBlock (c) 1998 Jochen Hoenicke
*
* You may distribute under the terms of the GNU General Public License.
*
* IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT,
* INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
* THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE
* HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
* BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
* SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* $Id$
*/
package jode.flow;
import jode.TabbedPrintWriter;
/**
* A TryBlock is created for each exception in the
* ExceptionHandlers-Attribute. <p>
*
* For each catch block (there may be more than one catch block
* appending a single try block) and for each finally and each
* synchronized block such a TryBlock is created. The
* finally/synchronized-blocks have a null exception type so that they
* are easily distinguishable from the catch blocks. <p>
*
* A TryBlock is an intermediate representation that gets
* converted later to a CatchBlock, a FinallyBlock or a
* SynchronizedBlock (after the body is parsed).
*
* @date 1998/09/16
* @author Jochen Hoenicke
* @see CatchBlock
* @see FinallyBlock
* @see SynchronizedBlock
*/
public class TryBlock extends StructuredBlock {
VariableSet gen;
StructuredBlock[] subBlocks = new StructuredBlock[1];
public TryBlock(FlowBlock tryFlow) {
this.gen = (VariableSet) tryFlow.gen.clone();
this.flowBlock = tryFlow;
StructuredBlock bodyBlock = tryFlow.block;
replace(bodyBlock);
this.subBlocks = new StructuredBlock[] { bodyBlock };
bodyBlock.outer = this;
tryFlow.lastModified = this;
}
public void addCatchBlock(CatchBlock catchBlock) {
StructuredBlock[] newSubBlocks =
new StructuredBlock[subBlocks.length+1];
System.arraycopy(subBlocks, 0, newSubBlocks, 0, subBlocks.length);
newSubBlocks[subBlocks.length] = catchBlock;
subBlocks = newSubBlocks;
catchBlock.outer = this;
catchBlock.setFlowBlock(flowBlock);
}
/**
* Replaces the given sub block with a new block.
* @param oldBlock the old sub block.
* @param newBlock the new sub block.
* @return false, if oldBlock wasn't a direct sub block.
*/
public boolean replaceSubBlock(StructuredBlock oldBlock,
StructuredBlock newBlock) {
for (int i=0; i<subBlocks.length; i++) {
if (subBlocks[i] == oldBlock) {
subBlocks[i] = newBlock;
return true;
}
}
return false;
}
/**
* Returns all sub block of this structured block.
*/
public StructuredBlock[] getSubBlocks() {
return subBlocks;
}
public void dumpInstruction(TabbedPrintWriter writer)
throws java.io.IOException {
writer.println("try {");
writer.tab();
subBlocks[0].dumpSource(writer);
writer.untab();
for (int i=1; i<subBlocks.length;i++)
subBlocks[i].dumpSource(writer);
writer.println("}");
}
}
Loading…
Cancel
Save