diff --git a/jode/jode/Decompiler.java b/jode/jode/Decompiler.java index 7d62fb3..7baf69d 100644 --- a/jode/jode/Decompiler.java +++ b/jode/jode/Decompiler.java @@ -24,6 +24,8 @@ import java.lang.reflect.Modifier; public class Decompiler { public static boolean isVerbose = false; public static boolean isDebugging = false; + public static boolean isTypeDebugging = false; + public static boolean showLVT = false; public static void main(String[] params) { JodeEnvironment env = new JodeEnvironment(); @@ -32,6 +34,10 @@ public class Decompiler { isVerbose = true; else if (params[i].equals("-debug")) isDebugging = true; + else if (params[i].equals("-type")) + isTypeDebugging = true; + else if (params[i].equals("-lvt")) + showLVT = true; else env.doClass(params[i]); } diff --git a/jode/jode/bytecode/Opcodes.java b/jode/jode/bytecode/Opcodes.java index 929db50..d35a49d 100644 --- a/jode/jode/bytecode/Opcodes.java +++ b/jode/jode/bytecode/Opcodes.java @@ -48,40 +48,65 @@ public abstract class Opcodes implements RuntimeConstants { }; - public static FlowBlock createNormal(int addr, int length, + public static FlowBlock createNormal(CodeAnalyzer ca, + int addr, int length, Instruction instr) { - return new FlowBlock(addr, length, + return new FlowBlock(ca, addr, length, new InstructionBlock(instr, new Jump(addr+length))); } - public static FlowBlock createGoto(int addr, int length, + public static FlowBlock createGoto(CodeAnalyzer ca, + int addr, int length, int destAddr) { - return new FlowBlock(addr, length, new EmptyBlock(new Jump(destAddr))); + return new FlowBlock(ca, addr, length, + new EmptyBlock(new Jump(destAddr))); } - public static FlowBlock createIfGoto(int addr, int length, + public static FlowBlock createJsr(CodeAnalyzer ca, + int addr, int length, + int destAddr) + { + return new FlowBlock(ca, addr, length, + new JsrBlock(new Jump(addr+length), + new Jump(destAddr))); + } + + public static FlowBlock createIfGoto(CodeAnalyzer ca, + int addr, int length, int destAddr, Instruction instr) { ConditionalBlock ifBlock = new ConditionalBlock(instr, new Jump(destAddr), new Jump(addr+length)); - return new FlowBlock(addr, length, ifBlock); + return new FlowBlock(ca, addr, length, ifBlock); } - public static FlowBlock createSwitch(int addr, int length, + public static FlowBlock createSwitch(CodeAnalyzer ca, + int addr, int length, int[] cases, int[] dests) { - return new FlowBlock(addr, length, new SwitchBlock(cases, dests)); + return new FlowBlock(ca, addr, length, + new SwitchBlock(new NopOperator(MyType.tInt), + cases, dests)); } - public static FlowBlock createBlock(int addr, int length, + public static FlowBlock createBlock(CodeAnalyzer ca, + int addr, int length, StructuredBlock block) { - return new FlowBlock(addr, length, block); + return new FlowBlock(ca, addr, length, block); + } + + public static FlowBlock createRet(CodeAnalyzer ca, + int addr, int length, + LocalInfo local) + { + return new FlowBlock(ca, addr, length, + new RetBlock(local)); } /** @@ -103,43 +128,43 @@ public abstract class Opcodes implements RuntimeConstants { int opcode = stream.readUnsignedByte(); switch (opcode) { case opc_nop: - return createNormal(addr, 1, new NopOperator()); + return createNormal(ca, addr, 1, new NopOperator()); case opc_aconst_null: return createNormal - (addr, 1, new ConstOperator(OBJECT_TYPE, "null")); + (ca, addr, 1, new ConstOperator(OBJECT_TYPE, "null")); case opc_iconst_m1: case opc_iconst_0: case opc_iconst_1: case opc_iconst_2: case opc_iconst_3: case opc_iconst_4: case opc_iconst_5: return createNormal - (addr, 1, new ConstOperator + (ca, addr, 1, new ConstOperator (ALL_INT_TYPE, Integer.toString(opcode - opc_iconst_0))); case opc_lconst_0: case opc_lconst_1: return createNormal - (addr, 1, new ConstOperator + (ca, addr, 1, new ConstOperator (LONG_TYPE, Integer.toString(opcode - opc_lconst_0) + "L")); case opc_fconst_0: case opc_fconst_1: case opc_fconst_2: return createNormal - (addr, 1, new ConstOperator + (ca, addr, 1, new ConstOperator (FLOAT_TYPE, Integer.toString(opcode - opc_fconst_0) + ".0F")); case opc_dconst_0: case opc_dconst_1: return createNormal - (addr, 1, new ConstOperator + (ca, addr, 1, new ConstOperator (DOUBLE_TYPE, Integer.toString(opcode - opc_dconst_0) + ".0")); case opc_bipush: return createNormal - (addr, 2, new ConstOperator + (ca, addr, 2, new ConstOperator (ALL_INT_TYPE, Integer.toString(stream.readByte()))); case opc_sipush: return createNormal - (addr, 3, new ConstOperator + (ca, addr, 3, new ConstOperator (ALL_INT_TYPE, Integer.toString(stream.readShort()))); case opc_ldc: { int index = stream.readUnsignedByte(); return createNormal - (addr, 2, new ConstOperator + (ca, addr, 2, new ConstOperator (ca.env.getConstantType(index), ca.env.getConstant(index).toString())); } @@ -147,14 +172,14 @@ public abstract class Opcodes implements RuntimeConstants { case opc_ldc2_w: { int index = stream.readUnsignedShort(); return createNormal - (addr, 3, new ConstOperator + (ca, addr, 3, new ConstOperator (ca.env.getConstantType(index), ca.env.getConstant(index).toString())); } case opc_iload: case opc_lload: case opc_fload: case opc_dload: case opc_aload: return createNormal - (addr, 2, new LocalLoadOperator + (ca, addr, 2, new LocalLoadOperator (types[0][opcode-opc_iload], ca.getLocalInfo(addr, stream.readUnsignedByte()))); case opc_iload_0: case opc_iload_1: case opc_iload_2: case opc_iload_3: @@ -163,19 +188,19 @@ public abstract class Opcodes implements RuntimeConstants { case opc_dload_0: case opc_dload_1: case opc_dload_2: case opc_dload_3: case opc_aload_0: case opc_aload_1: case opc_aload_2: case opc_aload_3: return createNormal - (addr, 1, new LocalLoadOperator + (ca, addr, 1, new LocalLoadOperator (types[0][(opcode-opc_iload_0)/4], ca.getLocalInfo(addr, (opcode-opc_iload_0) & 3))); case opc_iaload: case opc_laload: case opc_faload: case opc_daload: case opc_aaload: case opc_baload: case opc_caload: case opc_saload: return createNormal - (addr, 1, new ArrayLoadOperator + (ca, addr, 1, new ArrayLoadOperator (types[1][opcode - opc_iaload])); case opc_istore: case opc_lstore: case opc_fstore: case opc_dstore: case opc_astore: return createNormal - (addr, 2, new LocalStoreOperator + (ca, addr, 2, new LocalStoreOperator (types[0][opcode-opc_istore], ca.getLocalInfo(addr, stream.readUnsignedByte()), Operator.ASSIGN_OP)); @@ -190,7 +215,7 @@ public abstract class Opcodes implements RuntimeConstants { case opc_astore_0: case opc_astore_1: case opc_astore_2: case opc_astore_3: return createNormal - (addr, 1, new LocalStoreOperator + (ca, addr, 1, new LocalStoreOperator (types[0][(opcode-opc_istore_0)/4], ca.getLocalInfo(addr, (opcode-opc_istore_0) & 3), Operator.ASSIGN_OP)); @@ -198,43 +223,43 @@ public abstract class Opcodes implements RuntimeConstants { case opc_fastore: case opc_dastore: case opc_aastore: case opc_bastore: case opc_castore: case opc_sastore: return createNormal - (addr, 1, new ArrayStoreOperator + (ca, addr, 1, new ArrayStoreOperator (types[1][opcode - opc_iastore])); case opc_pop: case opc_pop2: return createNormal - (addr, 1, new PopOperator(opcode - opc_pop + 1)); + (ca, addr, 1, new PopOperator(opcode - opc_pop + 1)); case opc_dup: case opc_dup_x1: case opc_dup_x2: case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: return createNormal - (addr, 1, new DupOperator + (ca, addr, 1, new DupOperator ((opcode - opc_dup)%3, (opcode - opc_dup)/3+1)); case opc_swap: - return createNormal(addr, 1, new SwapOperator()); + return createNormal(ca, addr, 1, new SwapOperator()); case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: case opc_irem: case opc_lrem: case opc_frem: case opc_drem: return createNormal - (addr, 1, new BinaryOperator + (ca, addr, 1, new BinaryOperator (types[0][(opcode - opc_iadd)%4], (opcode - opc_iadd)/4+Operator.ADD_OP)); case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: return createNormal - (addr, 1, new UnaryOperator + (ca, addr, 1, new UnaryOperator (types[0][opcode - opc_ineg], Operator.NEG_OP)); case opc_ishl: case opc_lshl: case opc_ishr: case opc_lshr: case opc_iushr: case opc_lushr: return createNormal - (addr, 1, new ShiftOperator + (ca, addr, 1, new ShiftOperator (types[0][(opcode - opc_ishl)%2], (opcode - opc_ishl)/2 + Operator.SHIFT_OP)); case opc_iand: case opc_land: case opc_ior : case opc_lor : case opc_ixor: case opc_lxor: return createNormal - (addr, 1, new BinaryOperator + (ca, addr, 1, new BinaryOperator (types[0][(opcode - opc_iand)%2], (opcode - opc_iand)/2 + Operator.AND_OP)); case opc_iinc: { @@ -247,7 +272,7 @@ public abstract class Opcodes implements RuntimeConstants { } LocalInfo li = ca.getLocalInfo(addr, local); return createNormal - (addr, 3, new IIncOperator + (ca, addr, 3, new IIncOperator (li, Integer.toString(value), operation + Operator.OPASSIGN_OP)); } @@ -260,48 +285,46 @@ public abstract class Opcodes implements RuntimeConstants { if (to >= from) to++; return createNormal - (addr, 1, new ConvertOperator(types[0][from], + (ca, addr, 1, new ConvertOperator(types[0][from], types[0][to])); } case opc_i2b: case opc_i2c: case opc_i2s: return createNormal - (addr, 1, new ConvertOperator + (ca, addr, 1, new ConvertOperator (ALL_INT_TYPE, types[1][(opcode-opc_i2b)+5])); case opc_lcmp: case opc_fcmpl: case opc_fcmpg: case opc_dcmpl: case opc_dcmpg: return createNormal - (addr, 1, new CompareToIntOperator + (ca, addr, 1, new CompareToIntOperator (types[0][(opcode-opc_lcmp+3)/2], (opcode-opc_lcmp+3)%2)); case opc_ifeq: case opc_ifne: case opc_iflt: case opc_ifge: case opc_ifgt: case opc_ifle: return createIfGoto - (addr, 3, addr+stream.readShort(), + (ca, addr, 3, addr+stream.readShort(), new CompareUnaryOperator (ALL_INT_TYPE, opcode - opc_ifeq+Operator.COMPARE_OP)); case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmplt: case opc_if_icmpge: case opc_if_icmpgt: case opc_if_icmple: return createIfGoto - (addr, 3, addr+stream.readShort(), + (ca, addr, 3, addr+stream.readShort(), new CompareBinaryOperator (ALL_INT_TYPE, opcode - opc_if_icmpeq+Operator.COMPARE_OP)); case opc_if_acmpeq: case opc_if_acmpne: return createIfGoto - (addr, 3, addr+stream.readShort(), + (ca, addr, 3, addr+stream.readShort(), new CompareBinaryOperator (OBJECT_TYPE, opcode - opc_if_acmpeq+Operator.COMPARE_OP)); case opc_goto: return createGoto - (addr, 3, addr+stream.readShort()); -// case opc_jsr: -// return createGoto //XXX -// (addr, 3, addr+stream.readShort()); -// case opc_ret: -// return createReturn //XXX -// (addr, 2, -// new LocalLoadOperator -// (OBJECT_TYPE, -// ca.getLocalInfo(addr, stream.readUnsignedByte()))); + (ca, addr, 3, addr+stream.readShort()); + case opc_jsr: + return createJsr + (ca, addr, 3, addr+stream.readShort()); + case opc_ret: + return createRet + (ca, addr, 2, + ca.getLocalInfo(addr, stream.readUnsignedByte())); case opc_tableswitch: { int length = 3-(addr % 4); stream.skip(length); @@ -317,7 +340,7 @@ public abstract class Opcodes implements RuntimeConstants { dests[cases.length] = def; length += 13 + 4 * cases.length; return createSwitch - (addr, length, cases, dests); + (ca, addr, length, cases, dests); } case opc_lookupswitch: { int length = 3-(addr % 4); @@ -333,7 +356,7 @@ public abstract class Opcodes implements RuntimeConstants { dests[npairs] = def; length += 9 + 8 * npairs; return createSwitch - (addr, length, cases, dests); + (ca, addr, length, cases, dests); } case opc_ireturn: case opc_lreturn: case opc_freturn: case opc_dreturn: case opc_areturn: { @@ -341,23 +364,23 @@ public abstract class Opcodes implements RuntimeConstants { (ca.getMethod().mdef.getType().getReturnType(), types[0][opcode-opc_ireturn]); return createBlock - (addr, 1, new ReturnBlock(new NopOperator(retType))); + (ca, addr, 1, new ReturnBlock(new NopOperator(retType))); } case opc_return: /* Address -1 is interpreted as end of method */ return createBlock - (addr, 1, new EmptyBlock(new Jump(-1))); + (ca, addr, 1, new EmptyBlock(new Jump(-1))); case opc_getstatic: case opc_getfield: return createNormal - (addr, 3, new GetFieldOperator + (ca, addr, 3, new GetFieldOperator (ca, opcode == opc_getstatic, (FieldDefinition)ca.env.getConstant (stream.readUnsignedShort()))); case opc_putstatic: case opc_putfield: return createNormal - (addr, 3, new PutFieldOperator + (ca, addr, 3, new PutFieldOperator (ca, opcode == opc_putstatic, (FieldDefinition)ca.env.getConstant (stream.readUnsignedShort()))); @@ -365,14 +388,14 @@ public abstract class Opcodes implements RuntimeConstants { case opc_invokespecial: case opc_invokestatic : return createNormal - (addr, 3, new InvokeOperator + (ca, addr, 3, new InvokeOperator (ca, opcode == opc_invokestatic, opcode == opc_invokespecial, (FieldDefinition)ca.env.getConstant (stream.readUnsignedShort()))); case opc_invokeinterface: { FlowBlock fb = createNormal - (addr, 5, new InvokeOperator + (ca, addr, 5, new InvokeOperator (ca, false, false, (FieldDefinition)ca.env.getConstant (stream.readUnsignedShort()))); @@ -384,7 +407,7 @@ public abstract class Opcodes implements RuntimeConstants { ca.env.getConstant(stream.readUnsignedShort()); Type type = MyType.tClassOrArray(cldec.getName()); return createNormal - (addr, 3, new NewOperator(type, ca.env.getTypeString(type))); + (ca, addr, 3, new NewOperator(type, ca.env.getTypeString(type))); } case opc_newarray: { Type type; @@ -401,7 +424,7 @@ public abstract class Opcodes implements RuntimeConstants { throw new ClassFormatError("Invalid newarray operand"); } return createNormal - (addr, 2, + (ca, addr, 2, new NewArrayOperator(MyType.tArray(type), type.toString(), 1)); } @@ -411,22 +434,22 @@ public abstract class Opcodes implements RuntimeConstants { Identifier ident = cldec.getName(); Type type = MyType.tClassOrArray(cldec.getName()); return createNormal - (addr, 3, new NewArrayOperator + (ca, addr, 3, new NewArrayOperator (MyType.tArray(type), ca.env.getTypeString(type),1)); } case opc_arraylength: return createNormal - (addr, 1, new ArrayLengthOperator()); + (ca, addr, 1, new ArrayLengthOperator()); case opc_athrow: return createBlock - (addr, 1, + (ca, addr, 1, new ThrowBlock(new NopOperator(MyType.tUObject))); case opc_checkcast: { ClassDeclaration cldec = (ClassDeclaration) ca.env.getConstant (stream.readUnsignedShort()); Type type = MyType.tClassOrArray(cldec.getName()); return createNormal - (addr, 3, new CheckCastOperator + (ca, addr, 3, new CheckCastOperator (type, ca.env.getTypeString(type))); } case opc_instanceof: { @@ -434,28 +457,28 @@ public abstract class Opcodes implements RuntimeConstants { (stream.readUnsignedShort()); Type type = MyType.tClassOrArray(cldec.getName()); return createNormal - (addr, 3, + (ca, addr, 3, new InstanceOfOperator(type, ca.env.getTypeString(type))); } case opc_monitorenter: - return createNormal(addr, 1, + return createNormal(ca, addr, 1, new MonitorEnterOperator()); case opc_monitorexit: - return createNormal(addr, 1, + return createNormal(ca, addr, 1, new MonitorExitOperator()); case opc_wide: { switch (opcode=stream.readUnsignedByte()) { case opc_iload: case opc_lload: case opc_fload: case opc_dload: case opc_aload: return createNormal - (addr, 4, - new LocalLoadOperator(types[0][opcode-opc_iload], - new LocalInfo - (stream.readUnsignedShort()))); + (ca, addr, 4, + new LocalLoadOperator + (types[0][opcode-opc_iload], + ca.getLocalInfo(addr, stream.readUnsignedShort()))); case opc_istore: case opc_lstore: case opc_fstore: case opc_dstore: case opc_astore: return createNormal - (addr, 4, + (ca, addr, 4, new LocalStoreOperator (types[0][opcode-opc_istore], ca.getLocalInfo(addr, stream.readUnsignedShort()), @@ -470,16 +493,14 @@ public abstract class Opcodes implements RuntimeConstants { } LocalInfo li = ca.getLocalInfo(addr, local); return createNormal - (addr, 6, new IIncOperator + (ca, addr, 6, new IIncOperator (li, Integer.toString(value), operation + Operator.OPASSIGN_OP)); } -// case opc_ret: -// return new RetInstructionHeader -// (addr, 4, -// new LocalLoadOperator -// (INT_TYPE, -// ca.getLocalInfo(addr, stream.readUnsignedShort()))); + case opc_ret: + return createRet + (ca, addr, 4, + ca.getLocalInfo(addr, stream.readUnsignedShort())); default: throw new ClassFormatError("Invalid wide opcode "+opcode); } @@ -493,23 +514,23 @@ public abstract class Opcodes implements RuntimeConstants { for (int i=0; i 1)); - } - - if (doWhileFalse != null) { - doWhileFalse.replace(appendBlock, appendBlock); - doWhileFalse.setBody(appendBlock); - } - - /* Believe it or not: Now the rule, that the first part of a - * SequentialBlock shouldn't be another SequentialBlock is - * fulfilled.

- * - * This isn't easy to prove, it has a lot to do with the + /* appendBlock may be zero, if this is the switchcase with + * precedingcase = null. But in this case, there can't be + * any jumps. + */ + if (appendBlock != null) { + /* Now remove the jump of the appendBlock if it points to + * successor. + */ + if (appendBlock.jump != null + && appendBlock.jump.destination == succ) + appendBlock.removeJump(); + + /* If there are further jumps, put a do/while(0) block around + * appendBlock and replace every remaining jump with a break + * to the do/while block. + */ + LoopBlock doWhileFalse = null; + enum = successors.elements(); + while (enum.hasMoreElements()) { + Jump jump = (Jump) enum.nextElement(); + + if (jump == null || jump.destination != succ) + continue; + + if (doWhileFalse == null) + doWhileFalse = new LoopBlock(LoopBlock.DOWHILE, + LoopBlock.FALSE); + + int breaklevel = 1; + for (StructuredBlock surrounder = jump.prev.outer; + surrounder != appendBlock.outer; + surrounder = surrounder.outer) { + if (surrounder instanceof BreakableBlock) { + breaklevel++; + } + } + + SequentialBlock sequBlock = new SequentialBlock(); + StructuredBlock prevBlock = jump.prev; + prevBlock.removeJump(); + + sequBlock.replace(prevBlock, prevBlock); + sequBlock.setFirst(prevBlock); + sequBlock.setSecond(new BreakBlock(doWhileFalse, breaklevel > 1)); + } + + if (doWhileFalse != null) { + doWhileFalse.replace(appendBlock, appendBlock); + doWhileFalse.setBody(appendBlock); + } + } + + /* Believe it or not: Now the rule, that the first part of a + * SequentialBlock shouldn't be another SequentialBlock is + * fulfilled.

+ * + * This isn't easy to prove, it has a lot to do with the * transformation in optimizeJump and the fact that * appendBlock was the innermost Block containing all jumps * and lastModified. @@ -868,6 +901,21 @@ public class FlowBlock { /* Update the in/out-Vectors now */ VariableSet defineHere = updateInOut(this, false); + + while (lastModified != block) { + lastModified = lastModified.outer; + if (lastModified instanceof SequentialBlock + && lastModified.getSubBlocks()[0] + instanceof RawTryCatchBlock) { + + /* We leave the catch block of a raw-try-catch-block. + * We shall now create the Catch- resp. FinallyBlock. + */ + lastModified = + createCatchBlock((SequentialBlock)lastModified); + } + } + /* If there is only one jump to the beginning and it is the * last jump and (there is a do/while(0) block surrounding * everything but the last instruction, or the last @@ -1000,6 +1048,7 @@ public class FlowBlock { public void makeDeclaration(VariableSet param) { in.merge(param); in.subtract(param); + block.propagateUsage(); block.makeDeclaration(param); } diff --git a/jode/jode/flow/LoopBlock.java b/jode/jode/flow/LoopBlock.java index 45f2bdd..c891b9e 100644 --- a/jode/jode/flow/LoopBlock.java +++ b/jode/jode/flow/LoopBlock.java @@ -23,7 +23,7 @@ import jode.*; /** * This is the structured block for an Loop block. */ -public class LoopBlock extends BreakableBlock { +public class LoopBlock extends StructuredBlock implements BreakableBlock { public static final int WHILE = 0; public static final int DOWHILE = 1; @@ -130,6 +130,11 @@ public class LoopBlock extends BreakableBlock { public void dumpInstruction(TabbedPrintWriter writer) throws java.io.IOException { + if (label != null) { + writer.untab(); + writer.println(label+":"); + writer.tab(); + } boolean needBrace = ! (bodyBlock instanceof InstructionBlock); switch (type) { case WHILE: @@ -153,4 +158,43 @@ public class LoopBlock extends BreakableBlock { else if (needBrace) writer.println("}"); } + + + boolean mayChangeJump = true; + + /** + * The serial number for labels. + */ + static int serialno = 0; + + /** + * The label of this instruction, or null if it needs no label. + */ + String label = null; + + /** + * Returns the label of this block and creates a new label, if + * there wasn't a label previously. + */ + public String getLabel() { + if (label == null) + label = "while_"+(serialno++)+"_"; + return label; + } + + /** + * Is called by BreakBlock, to tell us that this block is breaked. + */ + public void setBreaked() { + mayChangeJump = false; + } + + /** + * 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 mayChangeJump; + } } diff --git a/jode/jode/flow/SequentialBlock.java b/jode/jode/flow/SequentialBlock.java index 206260c..4c43f68 100644 --- a/jode/jode/flow/SequentialBlock.java +++ b/jode/jode/flow/SequentialBlock.java @@ -63,6 +63,22 @@ public class SequentialBlock extends StructuredBlock { return getNextFlowBlock(); } + /** + * Make the declarations, i.e. initialize the declare variable + * to correct values. This will declare every variable that + * is marked as used, but not done. + * @param done The set of the already declare variables. + */ + public void makeDeclaration(VariableSet done) { + /* A sequential block is special, since it doesn't declare + * any local Variable, but let the first sub block do this. + */ + declare = new VariableSet(); + subBlocks[0].makeDeclaration(done); + done.addExact(used); + subBlocks[1].makeDeclaration(done); + } + public void dumpInstruction(TabbedPrintWriter writer) throws java.io.IOException { diff --git a/jode/jode/flow/StructuredBlock.java b/jode/jode/flow/StructuredBlock.java index b95c764..e890da9 100644 --- a/jode/jode/flow/StructuredBlock.java +++ b/jode/jode/flow/StructuredBlock.java @@ -70,7 +70,7 @@ public abstract class StructuredBlock { * The variable set containing all variables we must declare. * The analyzation is done in makeDeclaration */ - VariableSet declare = new VariableSet(); + VariableSet declare; /** * The surrounding structured block. If this is the outermost @@ -285,15 +285,10 @@ public abstract class StructuredBlock { } if (subs.length == 2) { /* All variables used in both sub blocks, are used in - * this block, too. But a sequential block is a notable - * exception, since it is enough if the first sub block - * declares the Variable - */ + * this block, too. + */ VariableSet newUse = childUse[0].intersectExact(childUse[1]); - if (this instanceof SequentialBlock) - subs[0].used.addExact(newUse); - else - used.addExact(newUse); + used.addExact(newUse); } return allUse; } @@ -305,8 +300,13 @@ public abstract class StructuredBlock { * @param done The set of the already declare variables. */ public void makeDeclaration(VariableSet done) { - propagateUsage(); - declare.addExact(used); + declare = new VariableSet(); + java.util.Enumeration enum = used.elements(); + while (enum.hasMoreElements()) { + LocalInfo local = ((LocalInfo) enum.nextElement()).getLocalInfo(); + if (!declare.contains(local)) + declare.addElement(local); + } declare.subtractExact(done); done.addExact(declare); diff --git a/jode/jode/flow/SwitchBlock.java b/jode/jode/flow/SwitchBlock.java index d8a9a84..0a41455 100644 --- a/jode/jode/flow/SwitchBlock.java +++ b/jode/jode/flow/SwitchBlock.java @@ -23,27 +23,82 @@ import jode.TabbedPrintWriter; /** * This is the structured block for an empty block. */ -public class SwitchBlock extends BreakableBlock { - int[] cases; - StructuredBlock[] caseBlocks; - - public SwitchBlock(int[] cases, int[] dests) { - this.cases = cases; - this.caseBlocks = new StructuredBlock[dests.length]; - for (int i=0; i=0; i--) { + /** + * Sort the destinations by finding the greatest destAddr + */ + int index = 0; + for (int j=1; j= dests[index]) + index = j; + } + + int value; + if (index == cases.length) + value = -1; + else + value = cases[index]; + + if (dests[index] == lastDest) + this.caseBlocks[i] = new CaseBlock(value); + else + this.caseBlocks[i] = new CaseBlock(value, + new Jump(dests[index])); + this.caseBlocks[i].outer = this; + this.caseBlocks[i].isLastBlock = lastBlock; + lastBlock = false; + lastDest = dests[index]; + dests[index] = -1; + if (index == cases.length) + this.caseBlocks[i].isDefault = true; } this.jump = null; mayChangeJump = true; } - public void dumpInstruction(TabbedPrintWriter writer) - throws java.io.IOException - { - writer.println("switch (/* XXX implement */)"); + /** + * Find the case that jumps directly to destination. + * @return The sub block of the case block, which jumps to destination. + */ + public StructuredBlock findCase(FlowBlock destination) { + for (int i=0; i < caseBlocks.length; i++) { + if (caseBlocks[i].subBlock != null + && caseBlocks[i].subBlock instanceof EmptyBlock + && caseBlocks[i].subBlock.jump != null + && caseBlocks[i].subBlock.jump.destination == destination) + + return caseBlocks[i].subBlock; + } + return null; } + /** + * Find the case that precedes the given case. + * @param block The sub block of the case, whose predecessor should + * be returned. + * @return The sub block of the case precedes the given case. + */ + public StructuredBlock prevCase(StructuredBlock block) { + for (int i=caseBlocks.length-1; i>=0; i--) { + if (caseBlocks[i].subBlock == block) { + for (i--; i>=0; i--) { + if (caseBlocks[i].subBlock != null) + return caseBlocks[i].subBlock; + } + } + } + return null; + } /** * Returns the block where the control will normally flow to, when @@ -53,20 +108,83 @@ public class SwitchBlock extends BreakableBlock { * the behaviour is undefined, so take care. * @return null, if the control flows to another FlowBlock. */ public StructuredBlock getNextBlock(StructuredBlock subBlock) { - /*XXX*/ + for (int i=0; i< caseBlocks.length-1; i++) { + if (subBlock == caseBlocks[i]) { + return caseBlocks[i+1]; + } + } return getNextBlock(); } public FlowBlock getNextFlowBlock(StructuredBlock subBlock) { - /*XXX*/ + for (int i=0; i< caseBlocks.length-1; i++) { + if (subBlock == caseBlocks[i]) { + return null; + } + } return getNextFlowBlock(); } + public void dumpInstruction(TabbedPrintWriter writer) + throws java.io.IOException + { + writer.println("switch ("+instr+") {"); + for (int i=0; i < caseBlocks.length; i++) + caseBlocks[i].dumpSource(writer); + writer.println("}"); + } + + public void setInstruction(jode.Instruction instr) { + super.setInstruction(instr); + sun.tools.java.Type type = instr.getType(); + if (type != caseBlocks[0].type) { + for (int i=0; i < caseBlocks.length; i++) + caseBlocks[i].type = type; + } + } + /** * Returns all sub block of this structured block. */ public StructuredBlock[] getSubBlocks() { return caseBlocks; } -} + boolean mayChangeJump = true; + + /** + * The serial number for labels. + */ + static int serialno = 0; + + /** + * The label of this instruction, or null if it needs no label. + */ + String label = null; + + /** + * Returns the label of this block and creates a new label, if + * there wasn't a label previously. + */ + public String getLabel() { + if (label == null) + label = "switch_"+(serialno++)+"_"; + return label; + } + + /** + * Is called by BreakBlock, to tell us that this block is breaked. + */ + public void setBreaked() { + mayChangeJump = false; + } + + /** + * 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 mayChangeJump; + } +} diff --git a/jode/jode/type/ClassRangeType.java b/jode/jode/type/ClassRangeType.java index 5cab4f4..a80be2a 100644 --- a/jode/jode/type/ClassRangeType.java +++ b/jode/jode/type/ClassRangeType.java @@ -238,6 +238,8 @@ public class ClassRangeType extends MyType { /** * Returns the generalized type of t1 and t2, e.g * tObject, tString -> tObject + * object , interface -> object + * since a sub class of object may implement interface * int , short -> int * tArray(tObject), tArray(tUnknown) -> tArray(tUnknown) * tArray(tUnknown), tObject -> tObject @@ -308,14 +310,14 @@ public class ClassRangeType extends MyType { ClassDeclaration c2 = new ClassDeclaration(t2.getClassName()); try { - /* if one of the two types is an interface which - * is implemented by the other type the interface - * is the result. + /* if one of the two types is an interface, return + * the other type, since at least a subtype of the + * other type may implement the interface. */ - if (c1.getClassDefinition(env).implementedBy(env, c2)) - return t1; - if (c2.getClassDefinition(env).implementedBy(env, c1)) + if (c1.getClassDefinition(env).isInterface()) return t2; + if (c2.getClassDefinition(env).isInterface()) + return t1; ClassDefinition c = c1.getClassDefinition(env); while(c != null && !c.superClassOf(env, c2)) { @@ -339,7 +341,11 @@ public class ClassRangeType extends MyType { " to <" + bottom + "-" + top + "> to "); Thread.dumpStack(); - } + } else if (Decompiler.isTypeDebugging) { + System.err.println("intersecting "+ this +" and "+ type + + " to <" + bottom + "-" + top + + "> to " + newType); + } return newType; } @@ -350,7 +356,7 @@ public class ClassRangeType extends MyType { public String typeString(String string, boolean flag1, boolean flag2) { - if (Decompiler.isDebugging) + if (Decompiler.isTypeDebugging) return "<"+bottomType+"-"+topType+">" + string; else if (bottomType != null) return bottomType.typeString(string, flag1, flag2); diff --git a/jode/jode/type/MyType.java b/jode/jode/type/MyType.java index 07ac1d6..6faff36 100644 --- a/jode/jode/type/MyType.java +++ b/jode/jode/type/MyType.java @@ -63,7 +63,7 @@ public class MyType extends Type { return new ClassRangeType(tObject, type); else if (typeCode == 103) return (((ClassRangeType)type).topType == null - ? tUnknown : new ClassRangeType(tObject, null)); + ? tUnknown : new ClassRangeType(tObject, type)); else return type; }