diff --git a/jode/Block.java b/jode/Block.java new file mode 100644 index 0000000..6cf395c --- /dev/null +++ b/jode/Block.java @@ -0,0 +1,18 @@ +package jode; + +public class Block extends Instruction { + Expression[] exprs; + + public Block(int addr, int length, Expression[] exprs) { + super(addr,length); + this.exprs = exprs; + } + + public void dumpSource(TabbedPrintWriter writer, CodeAnalyzer ca) + throws java.io.IOException + { + for (int i=0; i< exprs.length; i++) + exprs[i].dumpSource(writer,ca); + } +} + diff --git a/jode/jode/AssertError.java b/jode/jode/AssertError.java new file mode 100644 index 0000000..b67f3a0 --- /dev/null +++ b/jode/jode/AssertError.java @@ -0,0 +1,10 @@ +package jode; + +public class AssertError extends Error { + public AssertError() { + } + + public AssertError(String detail) { + super(detail); + } +} diff --git a/jode/jode/Decompiler.java b/jode/jode/Decompiler.java new file mode 100644 index 0000000..1facc7e --- /dev/null +++ b/jode/jode/Decompiler.java @@ -0,0 +1,12 @@ +package jode; +import sun.tools.java.*; +import java.lang.reflect.Modifier; + +public class Decompiler { + public static void main(String[] params) { + JodeEnvironment env = new JodeEnvironment(); + for (int i=0; i 0) + writer.print(modif + " "); + writer.print((cdef.isInterface())?"interface ":"class "); + writer.println(env.getNickName(cdef.getName().toString())); + writer.tab(); + if (cdef.getSuperClass() != null) + writer.println("extends "+cdef.getSuperClass().getName().toString()); + ClassDeclaration interfaces[] = cdef.getInterfaces(); + if (interfaces.length > 0) { + writer.print("implements "); + for (int i=0; i < interfaces.length; i++) { + if (i > 0) + writer.print(", "); + writer.print(interfaces[i].getName().toString()); + } + } + writer.untab(); + writer.println(" {"); + writer.tab(); + + for (int i=0; i< fields.length; i++) + fields[i].dumpSource(writer); + writer.untab(); + writer.println("}"); + } +} + + diff --git a/jode/jode/decompiler/CodeAnalyzer.java b/jode/jode/decompiler/CodeAnalyzer.java new file mode 100644 index 0000000..6fc495b --- /dev/null +++ b/jode/jode/decompiler/CodeAnalyzer.java @@ -0,0 +1,1016 @@ +package jode; +import sun.tools.java.*; +import java.lang.reflect.Modifier; +import java.util.*; +import java.io.*; + +public class CodeAnalyzer implements Analyzer, Constants, Opcodes { + + MethodAnalyzer method; + JodeEnvironment env; + BinaryCode bincode; + + Instruction[] instr; + int[] references; + Hashtable tryAddrs = new Hashtable(); + Hashtable catchAddrs = new Hashtable(); + Hashtable labels = new Hashtable(); + + public void readOpcodes(DataInputStream stream) + throws IOException, ClassFormatError + { + int addr = 0; + try { + while (true) { + int opcode; + try { + opcode = stream.readUnsignedByte(); + } catch (EOFException eof) { + break; + } + switch (opcode) { + case NOP_OP: + instr[addr] = new NopOperator(addr, 1); + break; + case ACONST_NULL_OP: + instr[addr] = new ConstOperator(addr, 1, OBJECT_TYPE, "null"); + break; + case ICONST_M1_OP: + case ICONST_0_OP: case ICONST_1_OP: case ICONST_2_OP: + case ICONST_3_OP: case ICONST_4_OP: case ICONST_5_OP: + instr[addr] = + new ConstOperator(addr, 1, ALL_INT_TYPE, + Integer.toString(opcode - ICONST_0_OP)); + break; + case LCONST_0_OP: case LCONST_1_OP: + instr[addr] = + new ConstOperator(addr, 1, LONG_TYPE, + Integer.toString(opcode - LCONST_0_OP)+ + "L"); + break; + case FCONST_0_OP: case FCONST_1_OP: case FCONST_2_OP: + instr[addr] = + new ConstOperator(addr, 1, FLOAT_TYPE, + Integer.toString(opcode - FCONST_0_OP)+ + ".0F"); + break; + case DCONST_0_OP: case DCONST_1_OP: + instr[addr] = + new ConstOperator(addr, 1, DOUBLE_TYPE, + Integer.toString(opcode - DCONST_0_OP)+ + ".0"); + break; + case BIPUSH_OP: + instr[addr] = + new ConstOperator(addr, 2, ALL_INT_TYPE, + Integer.toString(stream.readByte())); + break; + case SIPUSH_OP: + instr[addr] = + new ConstOperator(addr, 3, ALL_INT_TYPE, + Integer.toString(stream.readShort())); + break; + case LDC_OP: { + int index = stream.readUnsignedByte(); + instr[addr] = + new ConstOperator(addr, 2, env.getConstantType(index), + env.getConstant(index).toString()); + break; + } + case LDC_W_OP: + case LDC2_W_OP: { + int index = stream.readUnsignedShort(); + instr[addr] = + new ConstOperator(addr, 3, env.getConstantType(index), + env.getConstant(index).toString()); + break; + } + case ILOAD_OP: case LLOAD_OP: + case FLOAD_OP: case DLOAD_OP: case ALOAD_OP: + instr[addr] = + new LoadOperator(addr, 2, types[0][opcode-ILOAD_OP], + method.getLocal(stream.readUnsignedByte())); + break; + case ILOAD_0_OP: case ILOAD_1_OP: case ILOAD_2_OP: case ILOAD_3_OP: + case LLOAD_0_OP: case LLOAD_1_OP: case LLOAD_2_OP: case LLOAD_3_OP: + case FLOAD_0_OP: case FLOAD_1_OP: case FLOAD_2_OP: case FLOAD_3_OP: + case DLOAD_0_OP: case DLOAD_1_OP: case DLOAD_2_OP: case DLOAD_3_OP: + case ALOAD_0_OP: case ALOAD_1_OP: case ALOAD_2_OP: case ALOAD_3_OP: + instr[addr] = + new LoadOperator(addr, 1, types[0][(opcode-ILOAD_0_OP)/4], + method.getLocal((opcode-ILOAD_0_OP) & 3)); + break; + case IALOAD_OP: case LALOAD_OP: + case FALOAD_OP: case DALOAD_OP: case AALOAD_OP: + case BALOAD_OP: case CALOAD_OP: case SALOAD_OP: + instr[addr] = + new ArrayLoadOperator(addr, 1, types[1][opcode - IALOAD_OP]); + break; + case ISTORE_OP: case LSTORE_OP: + case FSTORE_OP: case DSTORE_OP: case ASTORE_OP: + instr[addr] = + new StoreOperator(addr, 2, types[0][opcode-ISTORE_OP], + method.getLocal(stream.readUnsignedByte()), + Operator.ASSIGN_OP); + break; + case ISTORE_0_OP: case ISTORE_1_OP: + case ISTORE_2_OP: case ISTORE_3_OP: + case LSTORE_0_OP: case LSTORE_1_OP: + case LSTORE_2_OP: case LSTORE_3_OP: + case FSTORE_0_OP: case FSTORE_1_OP: + case FSTORE_2_OP: case FSTORE_3_OP: + case DSTORE_0_OP: case DSTORE_1_OP: + case DSTORE_2_OP: case DSTORE_3_OP: + case ASTORE_0_OP: case ASTORE_1_OP: + case ASTORE_2_OP: case ASTORE_3_OP: + instr[addr] = + new StoreOperator(addr, 1, types[0][(opcode-ISTORE_0_OP)/4], + method.getLocal((opcode-ISTORE_0_OP) & 3), + Operator.ASSIGN_OP); + break; + case IASTORE_OP: case LASTORE_OP: + case FASTORE_OP: case DASTORE_OP: case AASTORE_OP: + case BASTORE_OP: case CASTORE_OP: case SASTORE_OP: + instr[addr] = + new ArrayStoreOperator(addr, 1, + types[1][opcode - IASTORE_OP]); + break; + case POP_OP: case POP2_OP: + instr[addr] = + new PopOperator(addr, 1, opcode - POP_OP + 1); + break; + case DUP_OP: case DUP_X1_OP: case DUP_X2_OP: + case DUP2_OP: case DUP2_X1_OP: case DUP2_X2_OP: + instr[addr] = + new DupOperator(addr, 1, + (opcode - DUP_OP)%3, + (opcode - DUP_OP)/3+1); + break; + case SWAP_OP: + instr[addr] = new SwapOperator(addr, 1); + break; + case IADD_OP: case LADD_OP: case FADD_OP: case DADD_OP: + case ISUB_OP: case LSUB_OP: case FSUB_OP: case DSUB_OP: + case IMUL_OP: case LMUL_OP: case FMUL_OP: case DMUL_OP: + case IDIV_OP: case LDIV_OP: case FDIV_OP: case DDIV_OP: + case IREM_OP: case LREM_OP: case FREM_OP: case DREM_OP: + instr[addr] = + new BinaryOperator(addr, 1, types[0][(opcode - IADD_OP)%4], + (opcode - IADD_OP)/4+Operator.ADD_OP); + break; + case INEG_OP: case LNEG_OP: case FNEG_OP: case DNEG_OP: + instr[addr] = + new UnaryOperator(addr, 1, types[0][opcode - INEG_OP], + Operator.NEG_OP); + break; + case ISHL_OP: case LSHL_OP: + case ISHR_OP: case LSHR_OP: + case IUSHR_OP: case LUSHR_OP: + instr[addr] = new ShiftOperator(addr, 1, + types[0][(opcode - ISHL_OP)%2], + (opcode - ISHL_OP)/2 + + Operator.SHIFT_OP); + break; + case IAND_OP: case LAND_OP: + case IOR_OP : case LOR_OP : + case IXOR_OP: case LXOR_OP: + instr[addr] = new BinaryOperator(addr, 1, + types[0][(opcode - ISHL_OP)%2], + (opcode - ISHL_OP)/2 + + Operator.SHIFT_OP); + break; + case IINC_OP: { + int local = stream.readUnsignedByte(); + int value = stream.readUnsignedByte(); + int operation = Operator.ADD_OP; + if (value < 0) { + value = -value; + operation = Operator.NEG_OP; + } + instr[addr] = new ConstOperator(addr, 1, ALL_INT_TYPE, + Integer.toString(value)); + instr[addr+1] = + new StoreOperator(addr+1, 2, ALL_INT_TYPE, + method.getLocal(local), + operation + Operator.OPASSIGN_OP); + addr++; + } + + break; + case I2L_OP: case I2F_OP: case I2D_OP: + case L2I_OP: case L2F_OP: case L2D_OP: + case F2I_OP: case F2L_OP: case F2D_OP: + case D2I_OP: case D2L_OP: case D2F_OP: { + int from = (opcode-I2L_OP)/3; + int to = (opcode-I2L_OP)%3; + if (to >= from) + to++; + instr[addr] = new ConvertOperator(addr, 1, types[0][from], + types[1][to]); + break; + } + case I2B_OP: case I2C_OP: case I2S_OP: + instr[addr] = new ConvertOperator(addr, 1, ALL_INT_TYPE, + types[1][(opcode-I2B_OP)+5]); + break; + case LCMP_OP: + case FCMPL_OP: case FCMPG_OP: + case DCMPL_OP: case DCMPG_OP: + instr[addr] = new CompareToIntOperator + (addr, 1, types[0][(opcode-LCMP_OP+3)/2], + (opcode-LCMP_OP+3)%2); + break; + case IFEQ_OP: case IFNE_OP: + case IFLT_OP: case IFGE_OP: case IFGT_OP: case IFLE_OP: { + instr[addr] = new CompareUnaryOperator + (addr, 1, ALL_INT_TYPE, + opcode - IFEQ_OP+Operator.COMPARE_OP); + instr[addr+1] = new IfGotoOperator(addr+1, 2, + addr+stream.readShort()); + addr++; + break; + } + case IF_ICMPEQ_OP: case IF_ICMPNE_OP: case IF_ICMPLT_OP: + case IF_ICMPGE_OP: case IF_ICMPGT_OP: case IF_ICMPLE_OP: + instr[addr] = new CompareBinaryOperator + (addr, 1, ALL_INT_TYPE, + opcode - IF_ICMPEQ_OP+Operator.COMPARE_OP); + instr[addr+1] = new IfGotoOperator(addr+1, 2, + addr+stream.readShort()); + addr++; + break; + case IF_ACMPEQ_OP: case IF_ACMPNE_OP: + instr[addr] = new CompareBinaryOperator + (addr, 1, OBJECT_TYPE, + opcode - IF_ACMPEQ_OP+Operator.COMPARE_OP); + instr[addr+1] = new IfGotoOperator(addr+1, 2, + addr+stream.readShort()); + addr++; + break; + case GOTO_OP: + instr[addr] = new GotoOperator(addr, 3, addr+stream.readShort()); + break; + case JSR_OP: + instr[addr] = new JsrOperator(addr, 3, addr+stream.readShort()); + break; + case RET_OP: + instr[addr] = new RetOperator(addr, 3, stream.readUnsignedByte()); + break; + case TABLESWITCH_OP: { + int length = 3-(addr % 4); + stream.skip(length); + int def = addr + stream.readInt(); + int low = stream.readInt(); + int high = stream.readInt(); + int[] cases = new int[high-low+1]; + int[] dests = new int[high-low+2]; + for (int i=0; i+low <= high; i++) { + cases[i] = i+low; + dests[i] = addr + stream.readInt(); + } + dests[cases.length] = def; + length += 13 + 4 * cases.length; + instr[addr] = new SwitchOperator(addr, length, cases, dests); + break; + } + case LOOKUPSWITCH_OP: { + int length = 3-(addr % 4); + stream.skip(length); + int def = addr + stream.readInt(); + int npairs = stream.readInt(); + int[] cases = new int[npairs]; + int[] dests = new int[npairs+1]; + for (int i=0; i < npairs; i++) { + cases[i] = stream.readInt(); + dests[i] = addr + stream.readInt(); + } + dests[npairs] = def; + length += 9 + 8 * npairs; + instr[addr] = new SwitchOperator(addr, length, cases, dests); + break; + } + case IRETURN_OP: case LRETURN_OP: + case FRETURN_OP: case DRETURN_OP: case ARETURN_OP: { + Type retType = UnknownType.commonType + (method.mdef.getType().getReturnType(), + types[0][opcode-IRETURN_OP]); + instr[addr] = + new ReturnOperator(addr, 1, retType); + break; + } + case RETURN_OP: + instr[addr] = new ReturnOperator(addr, 1, Type.tVoid); + break; + case GETSTATIC_OP: + instr[addr] = + new GetFieldOperator(addr, 3, true, + (FieldDefinition)env.getConstant + (stream.readUnsignedShort())); + break; + case PUTSTATIC_OP: + instr[addr] = + new PutFieldOperator(addr, 3, true, + (FieldDefinition)env.getConstant + (stream.readUnsignedShort())); + break; + case GETFIELD_OP: + instr[addr] = new GetFieldOperator(addr, 3, false, + (FieldDefinition)env.getConstant + (stream.readUnsignedShort())); + break; + case PUTFIELD_OP: + instr[addr] = + new PutFieldOperator(addr, 3, false, + (FieldDefinition)env.getConstant + (stream.readUnsignedShort())); + break; + case INVOKEVIRTUAL_OP: + case INVOKESPECIAL_OP: + case INVOKESTATIC_OP : + instr[addr] = new InvokeOperator(addr, 3, + opcode == INVOKESTATIC_OP, + opcode == INVOKESPECIAL_OP, + (FieldDefinition)env.getConstant + (stream.readUnsignedShort())); + break; + case INVOKEINTERFACE_OP: { + instr[addr] = new InvokeOperator(addr, 5, + false, false, + (FieldDefinition)env.getConstant + (stream.readUnsignedShort())); + int reserved = stream.readUnsignedShort(); + break; + } + case NEW_OP: { + ClassDeclaration cldec = (ClassDeclaration) + env.getConstant(stream.readUnsignedShort()); + instr[addr] = new NewOperator(addr, 3, + Type.tClass(cldec.getName())); + break; + } + case NEWARRAY_OP: { + Type type; + switch (stream.readUnsignedByte()) { + case 4: type = Type.tBoolean; break; + case 5: type = Type.tChar ; break; + case 6: type = Type.tFloat ; break; + case 7: type = Type.tDouble ; break; + case 8: type = Type.tByte ; break; + case 9: type = Type.tShort ; break; + case 10: type = Type.tInt ; break; + case 11: type = Type.tLong ; break; + default: + throw new ClassFormatError("Invalid newarray operand"); + } + instr[addr] = new NewArrayOperator(addr, 2, type); + break; + } + case ANEWARRAY_OP: { + ClassDeclaration cldec = (ClassDeclaration) env.getConstant + (stream.readUnsignedShort()); + instr[addr] = new NewArrayOperator + (addr, 3, Type.tClass(cldec.getName())); + break; + } + case ARRAYLENGTH_OP: + instr[addr] = new ArrayLengthOperator(addr, 1); + break; + case ATHROW_OP: + instr[addr] = new ThrowOperator(addr, 1); + break; + case CHECKCAST_OP: { + ClassDeclaration cldec = (ClassDeclaration) env.getConstant + (stream.readUnsignedShort()); + instr[addr] = new CheckCastOperator + (addr, 3, Type.tClass(cldec.getName())); + break; + } + case INSTANCEOF_OP: { + ClassDeclaration cldec = (ClassDeclaration) env.getConstant + (stream.readUnsignedShort()); + instr[addr] = new InstanceOfOperator + (addr, 3, Type.tClass(cldec.getName())); + break; + } + case MONITORENTER_OP: + instr[addr] = new MonitorEnterOperator(addr, 1); + break; + case MONITOREXIT_OP: + instr[addr] = new MonitorExitOperator(addr, 1); + break; + case WIDE_OP: { + switch (opcode=stream.readUnsignedByte()) { + case ILOAD_OP: case LLOAD_OP: + case FLOAD_OP: case DLOAD_OP: case ALOAD_OP: + instr[addr] = new LoadOperator(addr, 3, + types[0][opcode-ILOAD_OP], + method.getLocal + (stream.readUnsignedShort())); + break; + case ISTORE_OP: case LSTORE_OP: + case FSTORE_OP: case DSTORE_OP: case ASTORE_OP: + instr[addr] = new StoreOperator(addr, 3, + types[0][opcode-ISTORE_OP], + method.getLocal + (stream.readUnsignedShort()), + Operator.ASSIGN_OP); + break; + case IINC_OP: { + int local = stream.readUnsignedShort(); + int value = stream.readUnsignedShort(); + int operation = Operator.ADD_OP; + if (value < 0) { + value = -value; + operation = Operator.NEG_OP; + } + instr[addr] = new ConstOperator(addr, 1, ALL_INT_TYPE, + Integer.toString(value)); + instr[addr+1] = + new StoreOperator(addr+1, 4, ALL_INT_TYPE, + method.getLocal(local), + operation + Operator.OPASSIGN_OP); + addr++; + break; + } + case RET_OP: + instr[addr] = + new RetOperator(addr, 3, stream.readUnsignedShort()); + break; + default: + throw new ClassFormatError("Invalid wide opcode "+opcode); + } + break; + } + case MULTIANEWARRAY_OP: { + ClassDeclaration cldec = (ClassDeclaration) env.getConstant + (stream.readUnsignedShort()); + int dimension = stream.readUnsignedByte(); + instr[addr] = new NewArrayOperator + (addr, 3, Type.tClass(cldec.getName()), dimension); + break; + } + case IFNULL_OP: case IFNONNULL_OP: + instr[addr] = new CompareUnaryOperator + (addr, 1, OBJECT_TYPE, + opcode - IFNULL_OP + Operator.COMPARE_OP); + instr[addr+1] = new IfGotoOperator(addr+1, 2, + addr+stream.readShort()); + addr++; + break; + case GOTO_W_OP: + instr[addr] = new GotoOperator(addr, 5, addr + stream.readInt()); + break; + case JSR_W_OP: + instr[addr] = new JsrOperator(addr, 5, addr + stream.readInt()); + break; + default: + throw new ClassFormatError("Invalid opcode "+opcode); + } + addr += instr[addr].getLength(); + if (stream.available() != instr.length-addr) { + throw new RuntimeException("invalid op size: "+opcode); + } + } + } catch (ClassCastException ex) { + ex.printStackTrace(); + throw new ClassFormatError("Constant has wrong type"); + } + } + + void readCode(byte[] code) + throws ClassFormatError + { + instr = new Instruction[code.length]; + references = new int[code.length]; + try { + DataInputStream stream = + new DataInputStream(new ByteArrayInputStream(code)); + readOpcodes(stream); + } catch (IOException ex) { + throw new ClassFormatError(ex.toString()); + } + references[0]++; + for (int addr = 0; addr < code.length; + addr += instr[addr].getLength()) { + int[] successors = instr[addr].getSuccessors(); + for (int i=0; i < successors.length; i++) { + references[successors[i]]++; + } + } + } + + void setExceptionHandler(BinaryExceptionHandler handler) { + tryAddrs.put(new Integer(handler.startPC), handler); + references[handler.startPC]++; + catchAddrs.put(new Integer(handler.handlerPC), handler); + references[handler.handlerPC]++; + } + + Instruction getInstruction(int addr) { + return instr[addr]; + } + + void setInstruction(int addr, Instruction i) { + instr[addr] = i; + } + + void removeInstruction(int addr) { + instr[addr] = null; + } + + static int WRONG = -3; + static int SPECIAL = -2; + static int FIRST = -1; + + int getPreviousAddr(int addr) { + int i; + for (i = addr-1; i >= 0 && instr[i] == null; i--) {} + return i; + } + + int getNextAddr(int addr) { + return addr + instr[addr].getLength(); + } + + int getPredecessor(int addr) { + if (references[addr] != 1) + return WRONG; + if (addr == 0) + return FIRST; + if (catchAddrs.get(new Integer(addr)) != null) + return SPECIAL; + + int i = getPreviousAddr(addr); + if (instr[i].getLength() != addr-i) + throw new RuntimeException("length mismatch"); + int[] successors = instr[i].getSuccessors(); + for (int j=0; j< successors.length; j++) { + if (successors[j] == addr) + return i; + } + return WRONG; + } + + public void dumpSource(TabbedPrintWriter writer) + throws java.io.IOException + { + int[] successors = { 0}; + for (int addr = 0; addr < instr.length;) { + if (writer.verbosity > 5) { + writer.println("<"+addr + " - "+ + (addr+instr[addr].getLength()-1)+ + "> ["+references[addr]+"] : "+ + instr[addr].getClass().getName()); + writer.tab(); + } + int i; + for (i=0; i< successors.length && successors[i] != addr; i++) + {} + if (references[addr] != 1 || i == successors.length) + writer.print("addr_"+addr+": "); + instr[addr].dumpSource(writer, this); + + if (writer.verbosity > 5) + writer.untab(); + successors = instr[addr].getSuccessors(); + + for (i = instr[addr++].getLength()-1; i>0; i--) + if (instr[addr++] != null) + throw new AssertError ("dubious instr at addr "+addr); + } + } + + public CodeAnalyzer(MethodAnalyzer ma, BinaryCode bc, JodeEnvironment e) + throws ClassFormatError + { + method = ma; + env = e; + readCode(bincode.getCode()); + BinaryExceptionHandler[] handlers = bincode.getExceptionHandlers(); + for (int i=0; i=0; i--) { + addrs[i] = getPredecessor(addrs[i+1]); + if (addrs[i] < 0) + return -1; + exprs[i] = (Expression) instr[addrs[i]]; + if (exprs[i].isVoid()) + return -1; + } + length += addrs[params]-addrs[0]; + for (int i = 1; i <= params; i++) { + instr[addrs[i]] = null; + } + addr = addrs[0]; + instr[addr] = new Expression(addr, length, op, exprs); + return addr; + } catch (ClassCastException ex) { + return -1; + } + } + + public int combineExpressions(int addr) { + int count = 0; + int start = addr; + if (!(instr[addr] instanceof Expression) || + !((Expression)instr[addr]).isVoid()) + return -1; + do { + addr = getNextAddr(addr); + count++; + } while (addr < instr.length && + references[addr] == 1 && + instr[addr] instanceof Expression && + ((Expression)instr[addr]).isVoid()); + Expression[] expr = new Expression[count]; + addr = start; + for (int i=0; i < count; i++) { + expr[i] = (Expression)instr[addr]; + int next = getNextAddr(addr); + instr[addr] = null; + addr = next; + } + instr[start] = new Block(start, addr-start, expr); + return start; + } + + public int createAssignExpression(int addr) { + try { + StoreInstruction store = (StoreInstruction) instr[addr]; + int dupAddr = getPreviousAddr(addr); + if (dupAddr < 0 || references[dupAddr] != 1) + return -1; + DupOperator dup = (DupOperator) instr[dupAddr]; + if (dup.getDepth() != store.getLValueOperandCount() && + dup.getLength() != store.getLValueType().stackSize()) + return -1; + int end = getNextAddr(addr); + instr[dupAddr] = new AssignOperator(dupAddr, end-dupAddr, + Operator.ASSIGN_OP, store); + instr[addr] = null; + return dupAddr; + } catch (ClassCastException ex) { + return -1; + } + } + + public int createPostIncExpression(int addr) { + try { + int op; + Expression storeExpr = (Expression) instr[addr]; + StoreOperator store = (StoreOperator) storeExpr.getOperator(); + if (store.getOperator() == store.ADD_OP+store.OPASSIGN_OP) + op = Operator.INC_OP; + else if (store.getOperator() == store.NEG_OP+store.OPASSIGN_OP) + op = Operator.INC_OP+1; + else + return -1; + Expression expr = storeExpr.getSubExpressions()[0]; + ConstOperator constOp = (ConstOperator) expr.getOperator(); + if (!constOp.getValue().equals("1")) + return -1; + int loadAddr = getPreviousAddr(addr); + if (loadAddr < 0 || references[loadAddr] != 1) + return -1; + Expression loadExpr = (Expression) instr[loadAddr]; + LoadOperator load = (LoadOperator) loadExpr.getOperator(); + if (load.getSlot() != store.getSlot()) + return -1; + int end = getNextAddr(addr); + Operator postop = + new PostFixOperator(loadAddr, end-loadAddr, + loadExpr.getType(), op); + Expression [] exprs = { loadExpr }; + instr[loadAddr] = new Expression + (loadAddr, end-loadAddr, postop, exprs); + instr[addr] = null; + return loadAddr; + } catch (ClassCastException ex) { + return -1; + } + } + + public int createArrayOpAssign(int addr) { + try { + StoreInstruction store = (StoreInstruction) instr[addr]; + int binOpAddr = getPreviousAddr(addr); + if (binOpAddr < 0 || references[binOpAddr] != 1) + return -1; + BinaryOperator binop = (BinaryOperator) instr[binOpAddr]; + if (binop.getOperator() < binop.ADD_OP || + binop.getOperator() >= binop.ASSIGN_OP) + return -1; + int exprAddr = getPreviousAddr(binOpAddr); + if (exprAddr < 0 || references[exprAddr] != 1) + return -1; + Expression expr = (Expression) instr[exprAddr]; + int loadAddr = getPreviousAddr(exprAddr); + if (loadAddr < 0 || references[loadAddr] != 1) + return -1; + Operator load = + (Operator) instr[loadAddr]; + if (load.getType() != store.getLValueType()) + return -1; + if (!store.matches(load)) + return -1; + int dupAddr = getPreviousAddr(loadAddr); + if (dupAddr < 0) + return -1; + DupOperator dup = (DupOperator) instr[dupAddr]; + if (dup.getDepth() != 0 && + dup.getLength() != store.getLValueOperandCount()) + return -1; + int end = getNextAddr(addr); + instr[dupAddr] = new Expression + (dupAddr, binOpAddr-dupAddr, + expr.getOperator(), expr.getSubExpressions()); + instr[binOpAddr] = store; + store.setAddr(binOpAddr); + store.setLength(end-binOpAddr); + store.setOperator(store.OPASSIGN_OP+binop.getOperator()); + store.setLValueType + (UnknownType.commonType(binop.getType(), + store.getLValueType())); + instr[loadAddr] = instr[exprAddr] = + instr[addr] = null; + return dupAddr; + } catch (ClassCastException ex) { + return -1; + } + } + + public int combineNewConstructor(int addr) { + try { + InvokeOperator constrCall = (InvokeOperator) instr[addr]; + if (!constrCall.isConstructor()) + return -1; + int length = instr[addr].getLength(); + int params = constrCall.getOperandCount(); + Expression exprs[] = new Expression[params]; + int addrs[] = new int[params+1]; + addrs[params] = addr; + for (int i = params-1; i>0; i--) { + addrs[i] = getPredecessor(addrs[i+1]); + if (addrs[i] < 0) + return -1; + exprs[i] = (Expression) instr[addrs[i]]; + if (exprs[i].isVoid()) + return -1; + } + addrs[0] = getPredecessor(addrs[1]); + DupOperator dup = (DupOperator) instr[addrs[0]]; + if (dup.getCount() != 1 && dup.getDepth() != 0) + return -1; + addr = getPredecessor(addrs[0]); + exprs[0] = (Expression) instr[addr]; + if (exprs[0].isVoid()) + return -1; + NewOperator op = (NewOperator) exprs[0].getOperator(); + if (constrCall.getClassType() != op.getType()) + return -1; + length += addrs[params]-addr; + for (int i = 0; i <= params; i++) { + instr[addrs[i]] = null; + } + ConstructorOperator conOp = + new ConstructorOperator(addr, length, + constrCall.getClassType(), + constrCall.getField()); + instr[addr] = new Expression(addr, length, conOp, exprs); + return addr; + } catch (ClassCastException ex) { + return -1; + } + } + +// public int createIfGotoStatement(int addr) { +// if (references[addr] != 1) +// return -1; +// Expression e; +// int condAddr, dest; +// try { +// IfGotoOperator igo = (IfGotoOperator) instr[addr]; +// condAddr = getPreviousAddr(addr); +// if (condAddr < 0) +// return -1; +// e = (Expression) instr[condAddr]; +// if (e.isVoid()) +// return -1; +// dest = igo.getDestination(); +// } catch (ClassCastException ex) { +// return -1; +// } +// int end = getNextAddr(addr); +// instr[condAddr] = new IfGotoStatement(condAddr, end-condAddr, dest, e); +// instr[addr] = null; +// return condAddr; +// } + + public int combineIfGotoExpressions(int addr) { + Expression e[]; + int if1Addr; + int end = getNextAddr(addr); + int operator; + IfGotoOperator igo2; + try { + igo2 = (IfGotoOperator) + ((Expression)instr[addr]).getOperator(); + int dest = igo2.getDestination(); + if1Addr = getPreviousAddr(addr); + if (if1Addr < 0 || references[if1Addr] != 1) + return -1; + IfGotoOperator igo1 = (IfGotoOperator) + ((Expression)instr[if1Addr]).getOperator(); + if (igo1.getDestination() == end) { + e = new Expression[2]; + operator = Operator.LOG_AND_OP; + e[1] = ((Expression)instr[addr]).getSubExpressions()[0]; + e[0] = ((Expression)instr[if1Addr]). + getSubExpressions()[0].negate(); + references[end]--; + } else if (igo1.getDestination() == dest) { + e = new Expression[2]; + operator = Operator.LOG_OR_OP; + e[1] = ((Expression)instr[addr]).getSubExpressions()[0]; + e[0] = ((Expression)instr[if1Addr]).getSubExpressions()[0]; + references[dest]--; + } else + return -1; + } catch (ClassCastException ex) { + return -1; + } + Expression[] cond = + { new Expression(if1Addr, end-if1Addr, + new BinaryOperator(if1Addr, end-if1Addr, + Type.tBoolean, operator), e) }; + instr[if1Addr] = new Expression(if1Addr, end-if1Addr, igo2, cond); + instr[addr] = null; + return if1Addr; + } + + public int combineConditionalExpr(int addr) { + if (references[addr] != 1) + return -1; + Expression e[] = new Expression[3]; + int ifAddr, gotoAddr, e1Addr; + int end = getNextAddr(addr); + try { + e[2] = (Expression) instr[addr]; + if (e[2].isVoid()) + return -1; + gotoAddr = getPreviousAddr(addr); + if (gotoAddr < 0 || references[gotoAddr] != 1) + return -1; + if (((GotoOperator) + ((Expression)instr[gotoAddr]).getOperator()) + .getDestination() != end) + return -1; + e1Addr = getPreviousAddr(gotoAddr); + if (e1Addr <0 || references[e1Addr] != 1) + return -1; + e[1] = (Expression) instr[e1Addr]; + if (e[1].isVoid()) + return -1; + ifAddr = getPreviousAddr(e1Addr); + if (ifAddr < 0) + return -1; + e[0] = (Expression)instr[ifAddr]; + IfGotoOperator igo = (IfGotoOperator) e[0].getOperator(); + if (igo.getDestination() != addr) + return -1; + e[0] = e[0].getSubExpressions()[0].negate(); + } catch (ClassCastException ex) { + return -1; + } + IfThenElseOperator iteo = new IfThenElseOperator + (ifAddr, end-ifAddr, + UnknownType.commonType(e[1].getType(),e[2].getType())); + instr[ifAddr] = new Expression(ifAddr, end-ifAddr, iteo, e); + instr[e1Addr] = instr[gotoAddr] = instr[addr] = null; + references[end]--; + return ifAddr; + } + + public void analyzeLocals() + { + for (int slot=0; slot< bincode.maxLocals(); slot++) { + LocalVariable localVar = method.getLocal(slot); + int storeAddr[] = new int[instr.size]; + for (int i=0; i< instr.size; i++) { + storeAddr[i] = -1; + + Stack addrStack = new Stack(); + addrStack.push(new Integer(0)); + storeAddr[0] = 0; + + while (!addrStack.isEmpty()) { + int[] successors; + int addr = ((Integer)addrStack.pop()).intValue(); + + int store = infos[addr]; + if (instr[addr] instanceof StoreOperator) { + store = addr; + } else if (instr[addr] instanceof LoadOperator) { + localVar.combine(store, addr); + } + successors = instr[addr].getSuccessors(); + addr = (successors.length==1)?successor[0]; + + /* XXX try - catch blocks */ + + LocalInfo nextInfo = localVar.getInfo(store); + for (int i=0; i< successors.length; i++) { + if (info[successors[i]] != -1) { + if (nextInfo == localVar.getInfo(info[successors[i]])) + continue; + localVar.combine(info[successors[i]], store); + } else + info[successors[i]] = store; + addrStack.push(successors[i]); + } + } + } + } + + public void analyze() + { + for (int addr = 0; addr < instr.length; ) { + int nextAddr; + nextAddr = createExpression(addr); + if (nextAddr >= 0) { + addr = nextAddr; + continue; + } + nextAddr = createAssignExpression(addr); + if (nextAddr >= 0) { + addr = nextAddr; + continue; + } + nextAddr = createArrayOpAssign(addr); + if (nextAddr >= 0) { + addr = nextAddr; + continue; + } + nextAddr = createPostIncExpression(addr); + if (nextAddr >= 0) { + addr = nextAddr; + continue; + } + nextAddr = combineNewConstructor(addr); + if (nextAddr >= 0) { + addr = nextAddr; + continue; + } +// nextAddr = createIfGotoStatement(addr); +// if (nextAddr >= 0) { +// addr = nextAddr; +// continue; +// } + nextAddr = combineIfGotoExpressions(addr); + if (nextAddr >= 0) { + addr = nextAddr; + continue; + } + nextAddr = combineConditionalExpr(addr); + if (nextAddr >= 0) { + addr = nextAddr; + continue; + } + addr += instr[addr].getLength(); + } + for (int addr = 0; addr < instr.length; ) { + int nextAddr; + nextAddr = combineExpressions(addr); + if (nextAddr >= 0) { + addr = nextAddr; + continue; + } + addr += instr[addr].getLength(); + } + } + + public String getLocalName(int i, int addr) { + return method.getLocalName(i, addr).toString(); + } + + public String getTypeString(Type type) { + return type.toString(); + } + + public ClassDefinition getClassDefinition() { + return env.getClassDefinition(); + } +} + diff --git a/jode/jode/decompiler/FieldAnalyzer.java b/jode/jode/decompiler/FieldAnalyzer.java new file mode 100644 index 0000000..1ec371c --- /dev/null +++ b/jode/jode/decompiler/FieldAnalyzer.java @@ -0,0 +1,37 @@ +package jode; +import sun.tools.java.*; +import java.lang.reflect.Modifier; + +public class FieldAnalyzer implements Analyzer { + FieldDefinition fdef; + JodeEnvironment env; + + public FieldAnalyzer(FieldDefinition fd, JodeEnvironment e) + { + fdef = fd; + env = e; + } + + public void analyze() { + } + + public void dumpSource(TabbedPrintWriter writer) + throws java.io.IOException + { + String modif = Modifier.toString(fdef.getModifiers()); + if (modif.length() > 0) + writer.print(modif+" "); + + writer.println(fdef.getType(). + typeString(fdef.getName().toString(), false, false)+";"); +// writer.tab(); +// if (attributes.length > 0) { +// writer.println("/* Attributes: "+attributes.length+" */"); +// for (int i=0; i < attributes.length; i++) +// attributes[i].dumpSource(writer); +// } +// writer.untab(); + } +} + + diff --git a/jode/jode/decompiler/ImportHandler.java b/jode/jode/decompiler/ImportHandler.java new file mode 100644 index 0000000..ab8a70b --- /dev/null +++ b/jode/jode/decompiler/ImportHandler.java @@ -0,0 +1,96 @@ +package jode; +import sun.tools.java.*; +import sun.tools.util.*; +import java.util.*; + +public class JodeEnvironment extends LoadEnvironment { + Hashtable imports = new Hashtable(); + BinaryClass main; + Identifier pkg; + + boolean isVerbose = true; + + JodeEnvironment() { + super(null); + path = new ClassPath(System.getProperty("java.class.path")); + } + + public BinaryConstantPool getConstantPool() { + return main.getConstants(); + } + + public Object getConstant(int i) { + return main.getConstants().getConstant(i, this); + } + + public Type getConstantType(int i) + throws ClassFormatError + { + int t = main.getConstants().getConstantType(i); + switch(t) { + case 3: return Type.tInt ; + case 4: return Type.tFloat ; + case 5: return Type.tLong ; + case 6: return Type.tDouble; + case 8: return Type.tString; + default: + throw new ClassFormatError("invalid constant type: "+t); + } + } + + public String getNickName(String string) { + return string; + } + + public ClassDefinition getClassDefinition() { + return main; + } + + public void dumpHeader(TabbedPrintWriter writer) + throws java.io.IOException + { + writer.println("/* Decompiled by JoDe (Jochen's Decompiler) */"); + if (pkg != null && pkg != Constants.idNull) + writer.println("package "+pkg+";"); + Enumeration enum = imports.keys(); + while (enum.hasMoreElements()) { + Identifier packageName = (Identifier) enum.nextElement(); + Integer vote = (Integer) imports.get(packageName); + if (vote.intValue() > 3) + writer.println("import "+packageName+";"); + } + writer.println(""); + } + + public void error(String message) { + System.err.println(message); + } + + public void doClass(String className) + { + try { + Identifier ident = Identifier.lookup(className); + error(ident.toString()); + if (!classExists(ident)) { + error("`"+ident+"' not found"); + return; + } + pkg = ident.getQualifier(); + main = (BinaryClass)getClassDefinition(ident); + ClassAnalyzer a = new ClassAnalyzer(main, this); + a.analyze(); + TabbedPrintWriter writer = + new TabbedPrintWriter(System.out, " "); + a.dumpSource(writer); + } catch (ClassNotFound e) { + error(e.toString()); + } catch (java.io.IOException e) { + error(e.toString()); + } + } + + protected int loadFileFlags() + { + return 1; + } +} diff --git a/jode/jode/decompiler/LocalInfo.java b/jode/jode/decompiler/LocalInfo.java new file mode 100644 index 0000000..04ef789 --- /dev/null +++ b/jode/jode/decompiler/LocalInfo.java @@ -0,0 +1,15 @@ +package jode; +import sun.tools.java.*; + +public class LocalInfo { + static int serialnr = 0; + Identifier name; + Type type; + + public LocalInfo() { + name = Identifier.lookup("__"+serialnr); + type = Type.tUnknown; + serialnr++; + } +} + diff --git a/jode/jode/decompiler/LocalVariable.java b/jode/jode/decompiler/LocalVariable.java new file mode 100644 index 0000000..404e186 --- /dev/null +++ b/jode/jode/decompiler/LocalVariable.java @@ -0,0 +1,10 @@ +package jode; +import sun.tools.java.Type; +import sun.tools.java.Identifier; + +public interface LocalVariable { + public Identifier getName(int addr); + public Type getType(int addr); + public Type setType(int addr, Type type); + public void combine(int addr1, int addr2); +} diff --git a/jode/jode/decompiler/LocalVariableHash.java b/jode/jode/decompiler/LocalVariableHash.java new file mode 100644 index 0000000..2101600 --- /dev/null +++ b/jode/jode/decompiler/LocalVariableHash.java @@ -0,0 +1,44 @@ +package jode; +import sun.tools.java.Type; +import sun.tools.java.Identifier; + +public class LocalVariableHash implements LocalVariable { + Hashtable locals; + + private find(int addr) { + LocalInfo li = (LocalInfo) locals.get(new Integer(addr)); + if (li == null) { + li = new LocalInfo(); + locals.put(new Integer(addr), li); + } + return li; + } + + public Identifier getName(int addr) { + LocalInfo li = find(addr); + return li.name; + } + + public Type getType(int addr) { + LocalInfo li = find(addr); + return li.type; + } + + public Type setType(int addr, Type type) { + LocalInfo li = find(addr); + li.type = UnknownType.commonType(li.type, type); + return li.type; + } + + public void combine(int addr1, int addr2) { + LocalInfo li1 = find(addr1); + LocalInfo li2 = find(addr2); + li1.type = UnknownType.commonType(li1.type, li2.type); + Enumeration keys = locals.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + if (locals.get(key) == li2) + locals.put(key, li1); + } + } +} diff --git a/jode/jode/decompiler/LocalVariableRangeList.java b/jode/jode/decompiler/LocalVariableRangeList.java new file mode 100644 index 0000000..b8e9c5e --- /dev/null +++ b/jode/jode/decompiler/LocalVariableRangeList.java @@ -0,0 +1,82 @@ +package jode; +import sun.tools.java.*; + +public class LocalVariableRangeList implements LocalVariable { + + class MyLocalInfo extends LocalInfo { + int start; + int length; + MyLocalInfo next; + + MyLocalInfo(int s, int l, Identifier n, Type t) { + start = s; + length = l; + name = n; + type = t; + next = null; + } + } + + MyLocalInfo list = null; + int slot; + + LocalVariableRangeList(int slot) { + this.slot = slot; + } + + private void add(MyLocalInfo li) { + MyLocalInfo before = null; + MyLocalInfo after = list; + while (after != null && after.start < li.start) { + before = after; + after = after.next; + } + if (after != null && li.start + li.length > after.start) + throw new AssertError("non disjoint locals"); + li.next = after; + if (before == null) + list = li; + else + before.next = li; + } + + private LocalInfo find(int addr) { + MyLocalInfo li = list; + while (li != null && addr > li.start+li.length) + li = li.next; + if (li == null || li.start > addr) { + LocalInfo temp =new LocalInfo(); + return temp; + } + return li; + } + + public void addLocal(int start, int length, + Identifier name, Type type) { + MyLocalInfo li = new MyLocalInfo(start,length,name,type); + add (li); + } + + public Identifier getName(int addr) { + LocalInfo li = find(addr); + return li.name; + } + + public Type getType(int addr) { + LocalInfo li = find(addr); + return li.type; + } + + public LocalInfo getInfo(int addr) { + return find(addr); + } + + public Type setType(int addr, Type newType) { + LocalInfo li = find(addr); + return commonType(newType, li.type); + } + + public void combine(int addr1, int addr2) { + throw AssertError("combine called on RangeList"); + } +} diff --git a/jode/jode/decompiler/LocalVariableTable.java b/jode/jode/decompiler/LocalVariableTable.java new file mode 100644 index 0000000..ca811e8 --- /dev/null +++ b/jode/jode/decompiler/LocalVariableTable.java @@ -0,0 +1,55 @@ +package jode; +import sun.tools.java.*; +import java.io.*; +import java.util.Vector; + +public class LocalVariableTable { + Vector locals; + boolean readfromclass; + + public LocalVariableTable(int size) { + locals = new Vector(); + locals.setSize(size); + readfromclass = false; + } + + public boolean isReadFromClass() { + return readfromclass; + } + + public void read(JodeEnvironment env, DataInputStream stream) + throws IOException + { + int count = stream.readUnsignedShort(); + for (int i=0; i 0) + writer.print(modif+" "); + if (mdef.isInitializer()) { + writer.print(""); /* static block */ + } else { + if (mdef.isConstructor()) + writer.print(mdef.getClassDeclaration().getName().toString()); + else + writer.print(mdef.getType().getReturnType().toString()+" "+ + mdef.getName().toString()); + writer.print("("); + Type[] paramTypes = mdef.getType().getArgumentTypes(); + int offset = mdef.isStatic()?0:1; + for (int i=0; i0) + writer.print(", "); + writer.print(paramTypes[i]. + typeString(getLocalName(i+offset, 0).toString(), + false, false)); + } + writer.print(")"); + } + IdentifierToken[] exceptions = mdef.getExceptionIds(); + if (exceptions != null && exceptions.length > 0) { + writer.println(""); + writer.print("throws "); + for (int i= 0; i< exceptions.length; i++) { + if (exceptions[i] != null) { + if (i > 0) + writer.print(", "); + writer.print(exceptions[i].getName().toString()); + } + } + } + if (code != null) { + writer.println(" {"); + writer.tab(); + code.dumpSource(writer); + writer.untab(); + writer.println("}"); + } else + writer.println(";"); + } + + /* + public byte[] getAttribute(Identifier identifier) + { + if (mdef instanceof BinaryField) + return ((BinaryField)mdef).getAttribute(identifier); + return null; + } + */ +} diff --git a/jode/jode/decompiler/TabbedPrintWriter.java b/jode/jode/decompiler/TabbedPrintWriter.java new file mode 100644 index 0000000..765c5e7 --- /dev/null +++ b/jode/jode/decompiler/TabbedPrintWriter.java @@ -0,0 +1,42 @@ +package jode; +import java.io.*; + +public class TabbedPrintWriter { + boolean atbol; + String tabstr; + StringBuffer indent; + PrintWriter pw; + int verbosity=100; + + public TabbedPrintWriter (OutputStream os, String tabstr) { + pw = new PrintWriter(os); + this.tabstr=tabstr; + indent = new StringBuffer(); + atbol = true; + } + + public void tab() { + indent.append(tabstr); + } + + public void untab() { + indent.setLength(indent.length()-tabstr.length()); + } + + public void println(String str) throws java.io.IOException { + if (atbol) { + pw.print(indent); + } + pw.println(str); + pw.flush(); + atbol = true; + } + + public void print(String str) throws java.io.IOException { + if (atbol) { + pw.print(indent); + } + pw.print(str); + atbol = false; + } +} diff --git a/jode/jode/expr/ArrayLengthOperator.java b/jode/jode/expr/ArrayLengthOperator.java new file mode 100644 index 0000000..a2adbc7 --- /dev/null +++ b/jode/jode/expr/ArrayLengthOperator.java @@ -0,0 +1,36 @@ +package jode; +import sun.tools.java.*; + +public class ArrayLengthOperator extends Operator { + + Type arrayType; + + public ArrayLengthOperator(int addr, int length) { + super(addr,length, Type.tInt, 0); + arrayType = Type.tArray(UnknownType.tUnknown); + } + + public int getPriority() { + return 950; + } + + public int getOperandCount() { + return 1; + } + + public int getOperandPriority(int i) { + return 900; + } + + public Type getOperandType(int i) { + return arrayType; + } + + public void setOperandType(Type[] types) { + arrayType = UnknownType.commonType(arrayType,types[0]); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0] + ".length"; + } +} diff --git a/jode/jode/expr/ArrayLoadOperator.java b/jode/jode/expr/ArrayLoadOperator.java new file mode 100644 index 0000000..ca1b9d0 --- /dev/null +++ b/jode/jode/expr/ArrayLoadOperator.java @@ -0,0 +1,46 @@ +package jode; +import sun.tools.java.Type; +import sun.tools.java.ArrayType; + +public class ArrayLoadOperator extends SimpleOperator { + String value; + + public ArrayLoadOperator(int addr, int length, Type type) { + super(addr,length, type, 0, 2); + operandTypes[0] = Type.tArray(type); + operandTypes[1] = UnknownType.tUIndex; + } + + public int getPriority() { + return 950; + } + + public int getOperandPriority(int i) { + return (i==0)?950:0; + } + + /** + * Sets the return type of this operator. + * @return true if the operand types changed + */ + public boolean setType(Type type) { + if (type != this.type) { + super.setType(type); + operandTypes[0] = Type.tArray(type); + return true; + } + return false; + } + + public void setOperandType(Type[] t) { + super.setOperandType(t); + if (operandTypes[0] instanceof ArrayType) + type = operandTypes[0].getElementType(); + else + type = Type.tError; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0]+"["+operands[1]+"]"; + } +} diff --git a/jode/jode/expr/ArrayStoreOperator.java b/jode/jode/expr/ArrayStoreOperator.java new file mode 100644 index 0000000..6bf029e --- /dev/null +++ b/jode/jode/expr/ArrayStoreOperator.java @@ -0,0 +1,57 @@ +package jode; +import sun.tools.java.Type; +import sun.tools.java.ArrayType; + +public class ArrayStoreOperator extends StoreInstruction { + Type indexType; + + public ArrayStoreOperator(int addr, int length, Type type) { + super(addr,length, type); + indexType = UnknownType.tUIndex; + } + + public ArrayStoreOperator(int addr, int length, Type type, int operator) { + super(addr,length, type, operator); + indexType = UnknownType.tUIndex; + } + + public boolean matches(Operator loadop) { + return loadop instanceof ArrayLoadOperator; + } + + public int getLValueOperandCount() { + return 2; + } + + public int getLValueOperandPriority(int i) { + if (i == 0) + return 950; + else + return 0; + } + + public Type getLValueOperandType(int i) { + if (i == 0) + return Type.tArray(lvalueType); + else + return indexType; + } + + public void setLValueOperandType(Type[] t) { + indexType = UnknownType.commonType(indexType, t[1]); + Type arraytype = + UnknownType.commonType(t[0], Type.tArray(lvalueType)); + System.err.println("lvot: "+t[0]+","+Type.tArray(lvalueType)+ + " -> "+arraytype); + if (arraytype instanceof ArrayType) + lvalueType = arraytype.getElementType(); + else { + System.err.println("no array: "+arraytype); + lvalueType = Type.tError; + } + } + + public String getLValueString(CodeAnalyzer ca, String[] operands) { + return operands[0]+"["+operands[1]+"]"; + } +} diff --git a/jode/jode/expr/AssignOperator.java b/jode/jode/expr/AssignOperator.java new file mode 100644 index 0000000..da98438 --- /dev/null +++ b/jode/jode/expr/AssignOperator.java @@ -0,0 +1,46 @@ +package jode; +import sun.tools.java.Type; + +public class AssignOperator extends BinaryOperator { + StoreInstruction store; + + public AssignOperator(int addr, int length, int op, + StoreInstruction store) { + super(addr,length, store.getLValueType(), op); + this.store = store; + } + + public int getOperandCount() { + return store.getOperandCount(); + } + + public int getOperandPriority(int i) { + return store.getOperandPriority(i); + } + + public Type getOperandType(int i) { + return store.getOperandType(i); + } + + /** + * Sets the return type of this operator. + * @return true if the operand types changed + */ + public boolean setType(Type type) { + boolean result = store.setLValueType(type); + super.setType(store.getLValueType()); + return result; + } + + /** + * Overload this method if the resulting type depends on the input types + */ + public void setOperandType(Type[] inputTypes) { + store.setOperandType(inputTypes); + this.type = store.getLValueType(); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return store.toString(ca, operands); + } +} diff --git a/jode/jode/expr/BinaryOperator.java b/jode/jode/expr/BinaryOperator.java new file mode 100644 index 0000000..ec11347 --- /dev/null +++ b/jode/jode/expr/BinaryOperator.java @@ -0,0 +1,72 @@ +package jode; +import sun.tools.java.Type; + +public class BinaryOperator extends Operator { + protected Type operandType; + + public BinaryOperator(int addr, int length, Type type, int op) { + super(addr,length, type, op); + operandType = type; + } + + public int getOperandCount() { + return 2; + } + + public int getPriority() { + switch (operator) { + case 1: case 2: + return 610; + case 3: case 4: case 5: + return 650; + case 6: case 7: case 8: + return 600; + case 9: + return 450; + case 10: + return 410; + case 11: + return 420; + case 12: case 13: case 14: case 15: case 16: case 17: + case 18: case 19: case 20: case 21: case 22: case 23: + return 100; + case LOG_OR_OP: + return 310; + case LOG_AND_OP: + return 350; + } + throw new RuntimeException("Illegal operator"); + } + + public int getOperandPriority(int i) { + return getPriority() + i; + } + + public Type getOperandType(int i) { + return operandType; + } + + public void setOperandType(Type[] inputTypes) { + operandType = UnknownType.commonType + (operandType, UnknownType.commonType(inputTypes[0], + inputTypes[1])); + type = operandType; + } + + /** + * Sets the return type of this operator. + * @return true if the operand types changed + */ + public boolean setType(Type newType) { + operandType = UnknownType.commonType(operandType, newType); + if (type != operandType) { + type = operandType; + return true; + } + return false; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0] + " "+getOperatorString()+" "+ operands[1]; + } +} diff --git a/jode/jode/expr/CheckCastOperator.java b/jode/jode/expr/CheckCastOperator.java new file mode 100644 index 0000000..2e0e40c --- /dev/null +++ b/jode/jode/expr/CheckCastOperator.java @@ -0,0 +1,21 @@ +package jode; +import sun.tools.java.Type; + +public class CheckCastOperator extends SimpleOperator { + public CheckCastOperator(int addr, int length, Type type) { + super(addr,length, type, 0, 1); + operandTypes[0] = UnknownType.tSubClass(type); + } + + public int getPriority() { + return 700; + } + + public int getOperandPriority(int i) { + return getPriority(); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "("+ca.getTypeString(type) + ")" + operands[0]; + } +} diff --git a/jode/jode/expr/CompareBinaryOperator.java b/jode/jode/expr/CompareBinaryOperator.java new file mode 100644 index 0000000..891a403 --- /dev/null +++ b/jode/jode/expr/CompareBinaryOperator.java @@ -0,0 +1,38 @@ +package jode; +import sun.tools.java.Type; + +public class CompareBinaryOperator extends SimpleOperator { + public CompareBinaryOperator(int addr, int length, Type type, int op) { + super(addr,length, Type.tBoolean, op, 2); + operandTypes[0] = operandTypes[1] = type; + } + + public int getPriority() { + switch (getOperator()) { + case 26: + case 27: + return 500; + case 28: + case 29: + case 30: + case 31: + return 550; + } + throw new RuntimeException("Illegal operator"); + } + + public int getOperandPriority(int i) { + return getPriority()+i; + } + + public void setOperandType(Type[] inputTypes) { + super.setOperandType(inputTypes); + Type operandType = + UnknownType.commonType(operandTypes[0],operandTypes[1]); + operandTypes[0] = operandTypes[1] = operandType; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0] + " "+opString[operator]+" "+operands[1]; + } +} diff --git a/jode/jode/expr/CompareToIntOperator.java b/jode/jode/expr/CompareToIntOperator.java new file mode 100644 index 0000000..e927008 --- /dev/null +++ b/jode/jode/expr/CompareToIntOperator.java @@ -0,0 +1,27 @@ +package jode; +import sun.tools.java.Type; + +public class CompareToIntOperator extends BinaryOperator { + public CompareToIntOperator(int addr, int length, Type type, int op) { + super(addr,length, Type.tInt, op); + operandType = type; + } + + public int getPriority() { + switch (getOperator()) { + case 25: + case 26: + return 500; + case 27: + case 28: + case 29: + case 30: + return 550; + } + throw new RuntimeException("Illegal operator"); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0] + " "+opString[operator]+" "+operands[1]; + } +} diff --git a/jode/jode/expr/CompareUnaryOperator.java b/jode/jode/expr/CompareUnaryOperator.java new file mode 100644 index 0000000..881be5d --- /dev/null +++ b/jode/jode/expr/CompareUnaryOperator.java @@ -0,0 +1,39 @@ +package jode; +import sun.tools.java.Type; + +public class CompareUnaryOperator extends SimpleOperator { + public CompareUnaryOperator(int addr, int length, Type type, int op) { + super(addr,length, Type.tBoolean, op, 1); + operandTypes[0] = type; + } + + public int getPriority() { + switch (getOperator()) { + case 26: + case 27: + return 500; + case 28: + case 29: + case 30: + case 31: + return 550; + } + throw new RuntimeException("Illegal operator"); + } + + public int getOperandPriority(int i) { + return getPriority(); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + if (operandTypes[0] == Type.tBoolean) { + if (operator == 26) /* xx == false */ + return "! ("+operands[0]+")"; /*XXX Make operators */ + else if (operator == 27) /* xx != false */ + return operands[0]; + } + return operands[0] + " "+opString[operator]+" "+ + (UnknownType.isOfType(operandTypes[0], + UnknownType.tObject)?"null":"0"); + } +} diff --git a/jode/jode/expr/ConstOperator.java b/jode/jode/expr/ConstOperator.java new file mode 100644 index 0000000..aaf639d --- /dev/null +++ b/jode/jode/expr/ConstOperator.java @@ -0,0 +1,55 @@ +package jode; +import sun.tools.java.Type; + +public class ConstOperator extends NoArgOperator { + String value; + + public ConstOperator(int addr, int length, Type type, String value) { + super(addr, length, type); + this.value = value; + } + + public String getValue() { + return value; + } + + public int getPriority() { + return 1000; + } + + public String quoted(String str) { + StringBuffer result = new StringBuffer("\""); + for (int i=0; i< value.length(); i++) { + switch (value.charAt(i)) { + case '\t': + result.append("\\t"); + break; + case '\n': + result.append("\\n"); + break; + case '\\': + result.append("\\\\"); + break; + case '\"': + result.append("\\\""); + break; + default: + result.append(value.charAt(i)); + } + } + return result.append("\"").toString(); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + if (type == Type.tString) + return quoted(value); + if (type == Type.tBoolean) { + if (value.equals("0")) + return "false"; + else if (value.equals("1")) + return "true"; + } + return value; + } +} + diff --git a/jode/jode/expr/ConstructorOperator.java b/jode/jode/expr/ConstructorOperator.java new file mode 100644 index 0000000..c6a8ad1 --- /dev/null +++ b/jode/jode/expr/ConstructorOperator.java @@ -0,0 +1,45 @@ +package jode; +import sun.tools.java.*; + +public class ConstructorOperator extends Operator { + FieldDefinition field; + + public ConstructorOperator(int addr, int length, Type type, + FieldDefinition field) { + super(addr,length, type, 0); + this.field = field; + } + + public int getPriority() { + return 950; + } + + public int getOperandCount() { + return 1 + field.getType().getArgumentTypes().length; + } + + public int getOperandPriority(int i) { + if (i == 0) + return 950; + return 0; + } + + public Type getOperandType(int i) { + if (i == 0) + return type; + return field.getType().getArgumentTypes()[i-1]; + } + + public void setOperandType(Type types[]) { + } + + public String toString(CodeAnalyzer ca, String[] operands) { + StringBuffer result = new StringBuffer(operands[0]).append("("); + for (int i=0; i < field.getType().getArgumentTypes().length; i++) { + if (i>0) + result.append(", "); + result.append(operands[i+1]); + } + return result.append(")").toString(); + } +} diff --git a/jode/jode/expr/ConvertOperator.java b/jode/jode/expr/ConvertOperator.java new file mode 100644 index 0000000..4873d56 --- /dev/null +++ b/jode/jode/expr/ConvertOperator.java @@ -0,0 +1,36 @@ +package jode; +import sun.tools.java.Type; + +public class ConvertOperator extends Operator { + Type from; + + public ConvertOperator(int addr, int length, Type from, Type to) { + super(addr,length, to, 0); + this.from = from; + } + + public int getPriority() { + return 700; + } + + public int getOperandPriority(int i) { + return 700; + } + + public int getOperandCount() { + return 1; + } + + public Type getOperandType(int i) { + return from; + } + + public void setOperandType(Type[] inputTypes) { + from = UnknownType.commonType(from, inputTypes[0]); + } + + public String toString(CodeAnalyzer ca, String[] operands) + { + return "("+ca.getTypeString(type)+") "+operands[0]; + } +} diff --git a/jode/jode/expr/DupOperator.java b/jode/jode/expr/DupOperator.java new file mode 100644 index 0000000..f9e5db0 --- /dev/null +++ b/jode/jode/expr/DupOperator.java @@ -0,0 +1,25 @@ +package jode; + +public class DupOperator extends Instruction { + int count, depth; + + public DupOperator(int a, int l, int depth, int count) { + super(a,l); + this.count = count; + this.depth = depth; + } + + public int getCount(){ + return count; + } + + public int getDepth(){ + return depth; + } + + public void dumpSource(TabbedPrintWriter tpw, CodeAnalyzer ca) + throws java.io.IOException + { + tpw.println("dup"+count+"_x"+depth+";"); + } +} diff --git a/jode/jode/expr/Expression.java b/jode/jode/expr/Expression.java new file mode 100644 index 0000000..a12d6f2 --- /dev/null +++ b/jode/jode/expr/Expression.java @@ -0,0 +1,128 @@ +package jode; +import sun.tools.java.Type; + +public class Expression extends Instruction { + Operator operator; + Expression[] subExpressions; + + protected Expression(int addr, int length) { + super(addr, length); + } + + public Expression(int addr, int length, Operator op, Expression[] sub) { + super(addr, length); + operator = op; + subExpressions = sub; + if (op.getOperandCount() > 0) { + Type types[] = new Type[subExpressions.length]; + for (int i=0; i < types.length; i++) { + types[i] = subExpressions[i].getType(); + } + operator.setOperandType(types); + updateSubTypes(); + } + } + + public Expression negate() { + if (operator.operator >= operator.COMPARE_OP && + operator.operator < operator.COMPARE_OP+6) { + operator.setOperator(operator.getOperator() ^ 1); + return this; + } else if (operator.operator == operator.LOG_AND_OP || + operator.operator == operator.LOG_OR_OP) { + operator.setOperator(operator.getOperator() ^ 1); + for (int i=0; i< subExpressions.length; i++) { + subExpressions[i] = subExpressions[i].negate(); + } + return this; + } else if (operator.operator == operator.LOG_NOT_OP) { + return subExpressions[0]; + } + + Operator negop = + new UnaryOperator(getAddr(), getLength(), + Type.tBoolean, Operator.LOG_NOT_OP); + Expression[] e = { this }; + return new Expression(getAddr(), getLength(), negop, e); + } + + public Operator getOperator() { + return operator; + } + + public Expression[] getSubExpressions() { + return subExpressions; + } + + public Type getType() { + return operator.getType(); + } + + public void updateSubTypes() { + for (int i=0; i < subExpressions.length; i++) { + subExpressions[i].setType(operator.getOperandType(i)); + } + } + + public void setType(Type type) { + if (operator.setType(type)) + updateSubTypes(); + } + + public int[] getSuccessors() { + return operator.getSuccessors(); + } + + public boolean isVoid() { + return operator.getType() == Type.tVoid; + } + + String toString(CodeAnalyzer ca, int minPriority) { + String[] expr = new String[subExpressions.length]; + for (int i=0; i= 0 || + operator.casts.indexOf("<-",0) >= 0 && false) + result = "<"+operator.casts+" "+result+">"; + return result; + } + + public boolean equals(Expression expr) { + if (this == expr) + return true; + if (!operator.equals(expr.operator) || + subExpressions.length != expr.subExpressions.length) + return false; + for (int i=0; i 6) { + writer.print("< "+ + ca.getTypeString(operator.getType())+" "+ + operator.getClass().getName()+"("); + for (int i=0; i< subExpressions.length; i++) { + if (i>0) + writer.print(", "); + writer.print("("+ca.getTypeString(operator.getOperandType(i))+ + ") "+ca.getTypeString(subExpressions[i].getType())); + } + writer.println(") >"); + } + if (!isVoid()) + writer.print("push "); + writer.println(toString(ca, 0)+";"); + } +} diff --git a/jode/jode/expr/GetFieldOperator.java b/jode/jode/expr/GetFieldOperator.java new file mode 100644 index 0000000..22521fb --- /dev/null +++ b/jode/jode/expr/GetFieldOperator.java @@ -0,0 +1,56 @@ +package jode; +import sun.tools.java.*; + +public class GetFieldOperator extends Operator { + boolean staticFlag; + FieldDefinition field; + + public GetFieldOperator(int addr, int length, boolean staticFlag, + FieldDefinition field) { + super(addr, length, field.getType(), 0); + this.staticFlag = staticFlag; + this.field = field; + } + + public int getPriority() { + return 950; + } + + public int getOperandCount() { + return staticFlag?0:1; + } + + public int getOperandPriority(int i) { + if (staticFlag) { + /* shouldn't be called */ + throw new RuntimeException("Field is static"); + } + return 900; + } + + public Type getOperandType(int i) { + if (staticFlag) { + /* shouldn't be called */ + throw new RuntimeException("Field is static"); + } + return field.getClassDeclaration().getType(); + } + + public void setOperandType(Type types[]) { + } + + public String toString(CodeAnalyzer ca, String[] operands) { + String object; + if (staticFlag) { + if (field.getClassDefinition() == ca.getClassDefinition()) + return field.getName().toString(); + object = + ca.getTypeString(field.getClassDeclaration().getType()); + } else { + if (operands[0].equals("this")) + return field.getName().toString(); + object = operands[0]; + } + return object + "." + field.getName(); + } +} diff --git a/jode/jode/expr/GotoOperator.java b/jode/jode/expr/GotoOperator.java new file mode 100644 index 0000000..568b0f4 --- /dev/null +++ b/jode/jode/expr/GotoOperator.java @@ -0,0 +1,40 @@ +package jode; +import sun.tools.java.Type; + +public class GotoOperator extends JumpInstruction { + protected int destination; + + public GotoOperator(int addr, int length, int dest) { + super(addr,length); + this.destination = dest; + } + + public int getDestination() { + return destination; + } + + public int getOperandCount() { + return 0; + } + + public int getOperandPriority(int i) { + throw new AssertError("This operator has no operands"); + } + + public Type getOperandType(int i) { + throw new AssertError("This operator has no operands"); + } + + public void setOperandType(Type types[]) { + throw new AssertError("This operator has no operands"); + } + + public int[] getSuccessors() { + int [] result = { destination }; + return result; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "goto addr_" + destination; + } +} diff --git a/jode/jode/expr/IfGotoOperator.java b/jode/jode/expr/IfGotoOperator.java new file mode 100644 index 0000000..788f4c4 --- /dev/null +++ b/jode/jode/expr/IfGotoOperator.java @@ -0,0 +1,39 @@ +package jode; +import sun.tools.java.Type; + +public class IfGotoOperator extends JumpInstruction { + protected int destination; + + public IfGotoOperator(int addr, int length, int dest) { + super(addr,length); + destination = dest; + } + + public int getDestination() { + return destination; + } + + public int getOperandCount() { + return 1; + } + + public int getOperandPriority(int i) { + return 200; /* force parentheses around assignments */ + } + + public Type getOperandType(int i) { + return Type.tBoolean; + } + + public void setOperandType(Type types[]) { + } + + public int[] getSuccessors() { + int [] result = { destination, getAddr() + getLength() }; + return result; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "if ("+operands[0]+") goto addr_" + destination; + } +} diff --git a/jode/jode/expr/IfThenElseOperator.java b/jode/jode/expr/IfThenElseOperator.java new file mode 100644 index 0000000..145a2d0 --- /dev/null +++ b/jode/jode/expr/IfThenElseOperator.java @@ -0,0 +1,55 @@ +package jode; +import sun.tools.java.Type; + +public class IfThenElseOperator extends SimpleOperator { + public IfThenElseOperator(int addr, int length, Type type) { + super(addr,length, type, 0, 3); + operandTypes[0] = Type.tBoolean; + } + + public int getOperandCount() { + return 3; + } + + public int getPriority() { + return 200; + } + + public int getOperandPriority(int i) { + switch (i) { + case 0: + return 201; + case 1: + return 0; + case 2: + return 200; + default: + throw new AssertError("ifthenelse with operand "+i); + } + } + + public void setOperandType(Type[] inputTypes) { + super.setOperandType(inputTypes); + Type operandType = + UnknownType.commonType(operandTypes[1],operandTypes[2]); + type = operandTypes[1] = operandTypes[2] = operandType; + } + + /** + * Sets the return type of this operator. + * @return true if the operand types changed + */ + public boolean setType(Type newType) { + Type operandType = + UnknownType.commonType(operandTypes[1], newType); + if (type != operandType) { + type = operandTypes[1] = operandTypes[2] = operandType; + return true; + } + return false; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0] + " ? "+operands[1]+" : "+ operands[2]; + } +} diff --git a/jode/jode/expr/InstanceOfOperator.java b/jode/jode/expr/InstanceOfOperator.java new file mode 100644 index 0000000..4de74fc --- /dev/null +++ b/jode/jode/expr/InstanceOfOperator.java @@ -0,0 +1,27 @@ +package jode; +import sun.tools.java.Type; + +public class InstanceOfOperator extends SimpleOperator { + Type classType; + + public InstanceOfOperator(int addr, int length, Type type) { + super(addr, length, Type.tBoolean, 0, 1); + this.operandTypes[0] = UnknownType.tSubClass(type); + this.classType = type; + } + public int getOperandCount() { + return 1; + } + + public int getPriority() { + return 550; + } + + public int getOperandPriority(int i) { + return getPriority(); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0] + " instanceof "+ca.getTypeString(classType); + } +} diff --git a/jode/jode/expr/Instruction.java b/jode/jode/expr/Instruction.java new file mode 100644 index 0000000..4a34a7a --- /dev/null +++ b/jode/jode/expr/Instruction.java @@ -0,0 +1,34 @@ +package jode; + +public abstract class Instruction { + int addr,length; + + Instruction(int a, int l) { + addr = a; + length = l; + } + + public int getAddr() { + return addr; + } + + public void setAddr(int addr) { + this.addr = addr; + } + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + + public int[] getSuccessors() { + int[] result = { addr + length }; + return result; + } + + public abstract void dumpSource(TabbedPrintWriter tpw, CodeAnalyzer ca) + throws java.io.IOException; +} diff --git a/jode/jode/expr/InvokeOperator.java b/jode/jode/expr/InvokeOperator.java new file mode 100644 index 0000000..405853a --- /dev/null +++ b/jode/jode/expr/InvokeOperator.java @@ -0,0 +1,110 @@ +package jode; +import sun.tools.java.*; + +public class InvokeOperator extends Operator { + boolean staticFlag; + boolean specialFlag; + FieldDefinition field; + + public InvokeOperator(int addr, int length, + boolean staticFlag, boolean specialFlag, + FieldDefinition field) { + super(addr,length, field.getType().getReturnType(), 0); + this.staticFlag = staticFlag; + this.specialFlag = specialFlag; + this.field = field; + } + + public FieldDefinition getField() { + return field; + } + + public Type getClassType() { + return field.getClassDeclaration().getType(); + } + + public int getPriority() { + return 950; + } + + public int getOperandCount() { + return (staticFlag?0:1) + field.getType().getArgumentTypes().length; + } + + public int getOperandPriority(int i) { + if (!staticFlag && i == 0) + return 950; + return 0; + } + + public Type getOperandType(int i) { + if (!staticFlag) { + if (i == 0) + return field.getClassDeclaration().getType(); + i--; + } + return field.getType().getArgumentTypes()[i]; + } + + public void setOperandType(Type types[]) { + } + + public boolean isConstructor() { + return field.isConstructor(); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + String object; + int arg = 0; + if (staticFlag) { + if (field.getClassDefinition() == ca.getClassDefinition()) + object = ""; + else + object = ca. + getTypeString(field.getClassDeclaration().getType()); + } else { + if (operands[arg].equals("this")) { + if (specialFlag && + (field.getClassDeclaration() == + ca.getClassDefinition().getSuperClass() || + (field.getClassDeclaration().getName() == + Constants.idJavaLangObject && + ca.getClassDefinition().getSuperClass() == null))) + object = "super"; + else if (specialFlag) + object = "(("+ca.getTypeString + (field.getClassDeclaration().getType())+ + ") this)"; + else + object = ""; + } else { + if (specialFlag) + object = "(("+ca.getTypeString + (field.getClassDeclaration().getType())+ + ") "+operands[arg]+")"; + else + object = operands[arg]; + } + arg++; + } + String method; + if (isConstructor()) { + if (object.length() == 0) + method = "this"; + else + method = object; + } else { + if (object.length() == 0) + method = field.getName().toString(); + else + method = object+"."+field.getName().toString(); + } + StringBuffer params = new StringBuffer(); + for (int i=0; i < field.getType().getArgumentTypes().length; i++) { + if (i>0) + params.append(", "); + params.append(operands[arg++]); + } + return method+"("+params+")"; + } +} diff --git a/jode/jode/expr/JsrOperator.java b/jode/jode/expr/JsrOperator.java new file mode 100644 index 0000000..3d2a103 --- /dev/null +++ b/jode/jode/expr/JsrOperator.java @@ -0,0 +1,16 @@ +package jode; + +public class JsrOperator extends Instruction { + int destination; + + public JsrOperator(int addr, int length, int dest) { + super(addr,length); + this.destination = dest; + } + + public void dumpSource(TabbedPrintWriter writer, CodeAnalyzer ca) + throws java.io.IOException + { + writer.println("jsr addr_"+destination+";"); + } +} diff --git a/jode/jode/expr/JumpInstruction.java b/jode/jode/expr/JumpInstruction.java new file mode 100644 index 0000000..67165c0 --- /dev/null +++ b/jode/jode/expr/JumpInstruction.java @@ -0,0 +1,12 @@ +package jode; +import sun.tools.java.Type; + +public abstract class JumpInstruction extends Operator { + public JumpInstruction(int addr, int length) { + super(addr, length, Type.tVoid, 0); + } + + public int getPriority() { + return 0; + } +} diff --git a/jode/jode/expr/LoadOperator.java b/jode/jode/expr/LoadOperator.java new file mode 100644 index 0000000..c0ea62c --- /dev/null +++ b/jode/jode/expr/LoadOperator.java @@ -0,0 +1,25 @@ +package jode; +import sun.tools.java.Type; + +public class LoadOperator extends ConstOperator { + LocalVariable slot; + + public LoadOperator(int addr, int length, Type type, LocalVariable slot) { + super(addr,length, + UnknownType.commonType(type,slot.getType(addr)), ""); + this.slot = slot; + } + + public LocalVariable getSlot() { + return slot; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return slot.getName(getAddr()).toString(); + } + + public boolean equals(Object o) { + return (o instanceof LoadOperator && + ((LoadOperator) o).slot == slot); + } +} diff --git a/jode/jode/expr/MonitorEnterOperator.java b/jode/jode/expr/MonitorEnterOperator.java new file mode 100644 index 0000000..74d1d91 --- /dev/null +++ b/jode/jode/expr/MonitorEnterOperator.java @@ -0,0 +1,21 @@ +package jode; +import sun.tools.java.Type; + +public class MonitorEnterOperator extends SimpleOperator { + public MonitorEnterOperator(int a, int l) { + super(a,l, Type.tVoid, 0, 1); + operandTypes[0] = Type.tObject; + } + + public int getPriority() { + return 0; + } + + public int getOperandPriority(int i) { + return 0; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "monitorenter "+operands[0]; + } +} diff --git a/jode/jode/expr/MonitorExitOperator.java b/jode/jode/expr/MonitorExitOperator.java new file mode 100644 index 0000000..42c6cc2 --- /dev/null +++ b/jode/jode/expr/MonitorExitOperator.java @@ -0,0 +1,25 @@ +package jode; +import sun.tools.java.Type; + +public class MonitorExitOperator extends SimpleOperator { + public MonitorExitOperator(int a, int l) { + super(a,l,Type.tVoid, 0, 1); + operandTypes[0] = Type.tObject; + } + + public int getPriority() { + return 0; + } + + public int getOperandPriority(int i) { + return 0; + } + + public Type getOperandType(int i) { + return UnknownType.tObject; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "monitorexit "+operands[0]; + } +} diff --git a/jode/jode/expr/NewArrayOperator.java b/jode/jode/expr/NewArrayOperator.java new file mode 100644 index 0000000..29644c7 --- /dev/null +++ b/jode/jode/expr/NewArrayOperator.java @@ -0,0 +1,37 @@ +package jode; +import sun.tools.java.Type; + +public class NewArrayOperator extends SimpleOperator { + Type baseType; + + public NewArrayOperator(int addr, int length, + Type baseType, int dimensions) { + super(addr, length, baseType, 0, dimensions); + this.baseType = baseType; + for (int i=0; i< dimensions; i++) { + this.type = Type.tArray(this.type); + operandTypes[i] = UnknownType.tUIndex; + } + } + + public NewArrayOperator(int addr, int length, Type baseType) { + this(addr, length, baseType, 1); + } + + public int getPriority() { + return 900; + } + + public int getOperandPriority(int i) { + return 0; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + StringBuffer result + = new StringBuffer("new ").append(ca.getTypeString(baseType)); + for (int i=0; i< getOperandCount(); i++) { + result.append("[").append(operands[i]).append("]"); + } + return result.toString(); + } +} diff --git a/jode/jode/expr/NewConstructorOperator.java b/jode/jode/expr/NewConstructorOperator.java new file mode 100644 index 0000000..01256c7 --- /dev/null +++ b/jode/jode/expr/NewConstructorOperator.java @@ -0,0 +1,20 @@ +package jode; +import sun.tools.java.Type; + +public class NewConstructorOperator extends NoArgOperator { + Expression constructor; + + public NewConstructorOperator(int addr, int length, Type type, + Expression expr) { + super(addr,length, type); + this.constructor = expr; + } + + public int getPriority() { + return 950; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "new "+constructor.toString(ca, 0); + } +} diff --git a/jode/jode/expr/NewOperator.java b/jode/jode/expr/NewOperator.java new file mode 100644 index 0000000..ef15ff2 --- /dev/null +++ b/jode/jode/expr/NewOperator.java @@ -0,0 +1,17 @@ +package jode; +import sun.tools.java.Type; + +public class NewOperator extends NoArgOperator { + + public NewOperator(int addr, int length, Type type) { + super(addr,length, type); + } + + public int getPriority() { + return 950; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "new "+getType().toString(); + } +} diff --git a/jode/jode/expr/NoArgOperator.java b/jode/jode/expr/NoArgOperator.java new file mode 100644 index 0000000..0cc9981 --- /dev/null +++ b/jode/jode/expr/NoArgOperator.java @@ -0,0 +1,26 @@ +package jode; +import sun.tools.java.Type; + +public abstract class NoArgOperator extends Operator { + + public NoArgOperator(int addr, int length, Type type) { + super(addr, length, type, 0); + } + + public int getOperandCount() { + return 0; + } + + public int getOperandPriority(int i) { + throw new AssertError("This operator has no operands"); + } + + public Type getOperandType(int i) { + throw new AssertError("This operator has no operands"); + } + + public void setOperandType(Type[] types) { + throw new AssertError("This operator has no operands"); + } +} + diff --git a/jode/jode/expr/NopOperator.java b/jode/jode/expr/NopOperator.java new file mode 100644 index 0000000..a2f1aa7 --- /dev/null +++ b/jode/jode/expr/NopOperator.java @@ -0,0 +1,13 @@ +package jode; + +public class NopOperator extends Instruction { + public NopOperator(int a, int l) { + super(a,l); + } + + public void dumpSource(TabbedPrintWriter tpw, CodeAnalyzer ca) + throws java.io.IOException + { + tpw.println("nop;"); + } +} diff --git a/jode/jode/expr/Operator.java b/jode/jode/expr/Operator.java new file mode 100644 index 0000000..4797c30 --- /dev/null +++ b/jode/jode/expr/Operator.java @@ -0,0 +1,111 @@ +package jode; +import sun.tools.java.Type; + +public abstract class Operator extends Instruction { + public final static int ADD_OP = 1; + public final static int NEG_OP = 2; + public final static int SHIFT_OP = 6; + public final static int ASSIGN_OP = 12; + public final static int OPASSIGN_OP= 12; + public final static int INC_OP = 24; + public final static int COMPARE_OP = 26; /* must be even! */ + public final static int LOG_AND_OP = 32; /* must be even! */ + public final static int LOG_OR_OP = 33; + public final static int LOG_NOT_OP = 34; + static String opString[] = { + "", "+","-","*","/","%", "<<", ">>", ">>>", "&", "|", "^", + "=","+=","-=","*=","/=","%=", "<<=", ">>=", ">>>=", "&=", "|=", "^=", + "++", "--", + "==","!=","<",">=",">", "<=", "&&", "||", + "~", "!" + }; + + protected Type type; + protected int operator; + + String casts; + + Operator (int addr, int length, Type type, int op) { + super(addr,length); + this.type = type; + this.operator = op; + if (type == null) + throw new AssertError("type == null"); + casts = type.toString(); + } + + public int getOperator() { + return operator; + } + public void setOperator(int op) { + operator = op; + } + + /** + * Sets the return type of this operator. + * @return true if the operand types changed + */ + public boolean setType(Type type) { + if (!UnknownType.isOfType(type, this.type)) { + casts = type.toString()+"/*invalid*/ <- " + casts; + } else if (type != this.type) { + casts = type.toString()+" <- " + casts; + } + this.type = type; + return false; + } + + public final Type getType() { + return type; + } + + public String getOperatorString() { + return opString[operator]; + } + + /** + * Get priority of the operator. + * Currently this priorities are known: + *
  • 1000 constant + *
  • 950 new, .(field access), [] + *
  • 900 new[] + *
  • 800 ++,-- (post) + *
  • 700 ++,--(pre), +,-(unary), ~, !, cast + *
  • 650 *,/, % + *
  • 610 +,- + *
  • 600 <<, >>, >>> + *
  • 550 >, <, >=, <=, instanceof + *
  • 500 ==, != + *
  • 450 & + *
  • 420 ^ + *
  • 410 | + *
  • 350 && + *
  • 310 || + *
  • 200 ?: + *
  • 100 =, +=, -=, etc. + *
+ */ + public abstract int getPriority(); + + /** + * Get minimum priority of the nth operand. + * @see getPriority + */ + public abstract int getOperandPriority(int i); + public abstract Type getOperandType(int i); + public abstract int getOperandCount(); + public abstract void setOperandType(Type[] inputTypes); + public abstract String toString(CodeAnalyzer ca, String[] operands); + + public void dumpSource(TabbedPrintWriter writer, CodeAnalyzer ca) + throws java.io.IOException + { + if (type == null) + throw new AssertError("type == null"); + String[] operands = new String[getOperandCount()]; + for (int i=0; i< operands.length; i++) { + operands[i] = "stack_"+(operands.length-i-1); + } + writer.println(toString(ca, operands)); + } +} diff --git a/jode/jode/expr/PopOperator.java b/jode/jode/expr/PopOperator.java new file mode 100644 index 0000000..5fad913 --- /dev/null +++ b/jode/jode/expr/PopOperator.java @@ -0,0 +1,24 @@ +package jode; +import sun.tools.java.Type; + +public class PopOperator extends SimpleOperator { + int count; + + public PopOperator(int a, int l, int count) { + super(a,l, Type.tVoid, 0, 1); + operandTypes[0] = UnknownType.tUnknown; + this.count = count; + } + + public int getPriority() { + return 0; + } + + public int getOperandPriority(int i) { + return 0; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0]; + } +} diff --git a/jode/jode/expr/PostFixOperator.java b/jode/jode/expr/PostFixOperator.java new file mode 100644 index 0000000..f4b2635 --- /dev/null +++ b/jode/jode/expr/PostFixOperator.java @@ -0,0 +1,34 @@ +package jode; +import sun.tools.java.Type; + +public class PostFixOperator extends SimpleOperator { + public PostFixOperator(int addr, int length, Type type, int op) { + super(addr,length, type, op, 1); + } + + public int getPriority() { + return 800; + } + + public int getOperandPriority(int i) { + return getPriority(); + } + + /** + * Sets the return type of this operator. + * @return true if the operand types changed + */ + public boolean setType(Type type) { + super.setType(type); + Type newOpType = UnknownType.commonType(type, operandTypes[0]); + if (newOpType != operandTypes[0]) { + operandTypes[0] = newOpType; + return true; + } + return false; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return operands[0] + getOperatorString(); + } +} diff --git a/jode/jode/expr/PutFieldOperator.java b/jode/jode/expr/PutFieldOperator.java new file mode 100644 index 0000000..222d3db --- /dev/null +++ b/jode/jode/expr/PutFieldOperator.java @@ -0,0 +1,62 @@ +package jode; +import sun.tools.java.*; + +public class PutFieldOperator extends StoreInstruction { + boolean staticFlag; + FieldDefinition field; + + public PutFieldOperator(int addr, int length, boolean staticFlag, + FieldDefinition field) { + super(addr, length, field.getType()); + this.staticFlag = staticFlag; + this.field = field; + } + + public boolean matches(Operator loadop) { + return loadop instanceof GetFieldOperator && + ((GetFieldOperator)loadop).field == field; + } + + public int getLValueOperandCount() { + return staticFlag?0:1; + } + + public int getLValueOperandPriority(int i) { + if (staticFlag) { + /* shouldn't be called */ + throw new RuntimeException("Field is static"); + } + return 900; + } + + public Type getLValueOperandType(int i) { + if (staticFlag) { + /* shouldn't be called */ + throw new AssertError("Field is static"); + } + return field.getClassDefinition().getType(); + } + + public void setLValueOperandType(Type[] t) { + if (staticFlag) { + /* shouldn't be called */ + throw new AssertError("Field is static"); + } + return; + } + + public String getLValueString(CodeAnalyzer ca, String[] operands) { + String object; + if (staticFlag) { + if (field.getClassDefinition() == ca.getClassDefinition()) + return field.getName().toString(); + object = + ca.getTypeString(field.getClassDeclaration().getType())+"."; + } else { + if (operands[0].equals("this")) + return field.getName().toString(); + object = operands[0]; + } + return object + "." + field.getName(); + } +} diff --git a/jode/jode/expr/RetOperator.java b/jode/jode/expr/RetOperator.java new file mode 100644 index 0000000..27e1ddb --- /dev/null +++ b/jode/jode/expr/RetOperator.java @@ -0,0 +1,20 @@ +package jode; + +public class RetOperator extends Instruction { + int slot; + + public RetOperator(int addr, int length, int slot) { + super(addr,length); + this.slot = slot; + } + + public int[] getSuccessors() { + return new int[0]; + } + + public void dumpSource(TabbedPrintWriter writer, CodeAnalyzer ca) + throws java.io.IOException + { + writer.println("ret;"); + } +} diff --git a/jode/jode/expr/ReturnOperator.java b/jode/jode/expr/ReturnOperator.java new file mode 100644 index 0000000..b71f2f7 --- /dev/null +++ b/jode/jode/expr/ReturnOperator.java @@ -0,0 +1,29 @@ +package jode; +import sun.tools.java.Type; + +public class ReturnOperator extends SimpleOperator { + public ReturnOperator(int addr, int length, Type type) { + super(addr,length, Type.tVoid, 0, (type == Type.tVoid)?0:1); + if (type != Type.tVoid) + operandTypes[0] = type; + } + + public int[] getSuccessors() { + return new int[0]; + } + + public int getPriority() { + return 0; + } + + public int getOperandPriority(int i) { + return 0; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + StringBuffer result = new StringBuffer("return"); + if (getOperandCount() != 0) + result.append(" ").append(operands[0]); + return result.toString(); + } +} diff --git a/jode/jode/expr/ShiftOperator.java b/jode/jode/expr/ShiftOperator.java new file mode 100644 index 0000000..2dc6005 --- /dev/null +++ b/jode/jode/expr/ShiftOperator.java @@ -0,0 +1,23 @@ +package jode; +import sun.tools.java.Type; + +/** + * ShiftOpcodes are special, because their second operand is an UIndex + */ +public class ShiftOperator extends BinaryOperator { + protected Type shiftType; + + public ShiftOperator(int addr, int length, Type type, int op) { + super(addr,length, type, op); + shiftType = UnknownType.tUIndex; + } + + public Type getOperandType(int i) { + return (i==0)?operandType:shiftType; + } + + public void setOperandType(Type[] inputTypes) { + operandType = UnknownType.commonType(operandType, inputTypes[0]); + shiftType = UnknownType.commonType(shiftType, inputTypes[1]); + } +} diff --git a/jode/jode/expr/SimpleOperator.java b/jode/jode/expr/SimpleOperator.java new file mode 100644 index 0000000..a3b4817 --- /dev/null +++ b/jode/jode/expr/SimpleOperator.java @@ -0,0 +1,31 @@ +package jode; +import sun.tools.java.Type; + +public abstract class SimpleOperator extends Operator { + protected Type[] operandTypes; + + public SimpleOperator(int addr, int length, Type type, int operator, + int operandCount) { + super(addr, length, type, operator); + operandTypes = new Type[operandCount]; + for (int i=0; i< operandCount; i++) { + operandTypes[i] = type; + } + } + + public int getOperandCount() { + return operandTypes.length; + } + + public Type getOperandType(int i) { + return operandTypes[i]; + } + + public void setOperandType(Type[] t) { + for (int i=0; i< operandTypes.length; i++) { + if (UnknownType.commonType(operandTypes[i], t[i]) == Type.tError) + System.err.println("Error: "+operandTypes[i]+","+t[i]); + operandTypes[i] = UnknownType.commonType(operandTypes[i], t[i]); + } + } +} diff --git a/jode/jode/expr/StoreInstruction.java b/jode/jode/expr/StoreInstruction.java new file mode 100644 index 0000000..8af333c --- /dev/null +++ b/jode/jode/expr/StoreInstruction.java @@ -0,0 +1,80 @@ +package jode; +import sun.tools.java.Type; + +public abstract class StoreInstruction extends Operator { + + public String lvCasts; + Type lvalueType; + + public StoreInstruction(int addr, int length, Type type) { + this (addr,length, type, ASSIGN_OP); + } + + public StoreInstruction(int addr, int length, Type type, int operator) { + super(addr,length, Type.tVoid, operator); + lvalueType = type; + lvCasts = lvalueType.toString(); + } + + public Type getLValueType() { + return lvalueType; + } + + public abstract boolean matches(Operator loadop); + public abstract int getLValueOperandCount(); + public abstract int getLValueOperandPriority(int i); + public abstract Type getLValueOperandType(int i); + public abstract void setLValueOperandType(Type [] t); + + /** + * Sets the type of the lvalue (and rvalue). + * @return true if the operand types changed + */ + public boolean setLValueType(Type type) { + if (!UnknownType.isOfType(type, this.lvalueType)) { + lvCasts = type.toString()+"/*invalid*/ <- " + lvCasts; + } else if (type != this.lvalueType) { + lvCasts = type.toString()+" <- " + lvCasts; + } + this.lvalueType = type; + return false; + } + + public abstract String getLValueString(CodeAnalyzer ca, String[] operands); + + public int getPriority() { + return 100; + } + + public int getOperandPriority(int i) { + if (i == getLValueOperandCount()) + return 100; + else + return getLValueOperandPriority(i); + } + + public Type getOperandType(int i) { + if (i == getLValueOperandCount()) + return getLValueType(); + else + return getLValueOperandType(i); + } + + public void setOperandType(Type[] t) { + if (getLValueOperandCount() > 0) + setLValueOperandType(t); + setLValueType + (UnknownType.commonType(lvalueType, t[getLValueOperandCount()])); + } + + public int getOperandCount() { + return 1 + getLValueOperandCount(); + } + + public String toString(CodeAnalyzer ca, String[] operands) + { + return "{"+lvCasts+" "+getLValueString(ca, operands) + "} "+ + getOperatorString() +" "+ + operands[getLValueOperandCount()]; + } +} diff --git a/jode/jode/expr/StoreOperator.java b/jode/jode/expr/StoreOperator.java new file mode 100644 index 0000000..89139b6 --- /dev/null +++ b/jode/jode/expr/StoreOperator.java @@ -0,0 +1,46 @@ +package jode; +import sun.tools.java.Type; + +public class StoreOperator extends StoreInstruction { + LocalVariable slot; + + public StoreOperator(int addr, int length, Type type, + LocalVariable slot, int operator) { + super(addr,length, + UnknownType.commonType(type,slot.getType(addr+length)), + operator); + this.slot = slot; + } + + public LocalVariable getSlot() { + return slot; + } + + public boolean matches(Operator loadop) { + return loadop instanceof LoadOperator && + ((LoadOperator)loadop).getSlot() == slot; + } + + public int getLValueOperandCount() { + return 0; + } + + public int getLValueOperandPriority(int i) { + /* shouldn't be called */ + throw new RuntimeException("StoreOperator has no operands"); + } + + public Type getLValueOperandType(int i) { + /* shouldn't be called */ + throw new RuntimeException("StoreOperator has no operands"); + } + + public void setLValueOperandType(Type []t) { + /* shouldn't be called */ + throw new RuntimeException("StoreOperator has no operands"); + } + + public String getLValueString(CodeAnalyzer ca, String[] operands) { + return slot.getName(getAddr()+getLength()).toString(); + } +} diff --git a/jode/jode/expr/SwapOperator.java b/jode/jode/expr/SwapOperator.java new file mode 100644 index 0000000..df67646 --- /dev/null +++ b/jode/jode/expr/SwapOperator.java @@ -0,0 +1,13 @@ +package jode; + +public class SwapOperator extends Instruction { + public SwapOperator(int a, int l) { + super(a,l); + } + + public void dumpSource(TabbedPrintWriter writer, CodeAnalyzer ca) + throws java.io.IOException + { + writer.println("swap;"); + } +} diff --git a/jode/jode/expr/SwitchOperator.java b/jode/jode/expr/SwitchOperator.java new file mode 100644 index 0000000..d46e680 --- /dev/null +++ b/jode/jode/expr/SwitchOperator.java @@ -0,0 +1,70 @@ +package jode; +import sun.tools.java.Type; + +public class SwitchOperator extends JumpInstruction { + int[] cases; + int[] destinations; + Type operandType; + + public SwitchOperator(int addr, int length, int[] cases, int[] dests) { + super(addr,length); + this.cases = cases; + this.destinations = dests; + this.operandType = UnknownType.tUInt; + } + + public int[] getCases() { + return cases; + } + + public int[] getSuccessors() { + return destinations; + } + + public int getPriority() { + return 0; + } + + public int getOperandCount() { + return 1; + } + + public int getOperandPriority(int i) { + return 0; + } + + public Type getOperandType(int i) { + return operandType; + } + + public void setOperandType(Type types[]) { + operandType = UnknownType.commonType(operandType, types[0]); + } + + public boolean setType(Type t) { + super.setType(type); + if (type != operandType) { + operandType = type; + return true; + } + return false; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "switch ("+operands[0]+") "; + } + + public void dumpSource(TabbedPrintWriter writer, CodeAnalyzer ca) + throws java.io.IOException + { + writer.println("switch(stack_0) {"); + writer.tab(); + for (int i=0; i< cases.length; i++) { + writer.println("case "+cases[i]+ + ": goto addr_"+destinations[i]+";"); + } + writer.println("default: goto addr_"+destinations[cases.length]); + writer.untab(); + writer.println("}"); + } +} diff --git a/jode/jode/expr/ThrowOperator.java b/jode/jode/expr/ThrowOperator.java new file mode 100644 index 0000000..3995219 --- /dev/null +++ b/jode/jode/expr/ThrowOperator.java @@ -0,0 +1,13 @@ +package jode; +import sun.tools.java.Type; + +public class ThrowOperator extends ReturnOperator { + + public ThrowOperator(int addr, int length) { + super(addr,length, UnknownType.tUObject); + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return "throw " + operands[0]; + } +} diff --git a/jode/jode/expr/UnaryOperator.java b/jode/jode/expr/UnaryOperator.java new file mode 100644 index 0000000..e25627e --- /dev/null +++ b/jode/jode/expr/UnaryOperator.java @@ -0,0 +1,34 @@ +package jode; +import sun.tools.java.Type; + +public class UnaryOperator extends SimpleOperator { + public UnaryOperator(int addr, int length, Type type, int op) { + super(addr,length, type, op, 1); + } + + public int getPriority() { + return 700; + } + + public int getOperandPriority(int i) { + return getPriority(); + } + + /** + * Sets the return type of this operator. + * @return true if the operand types changed + */ + public boolean setType(Type type) { + super.setType(type); + Type newOpType = UnknownType.commonType(type, operandTypes[0]); + if (newOpType != operandTypes[0]) { + operandTypes[0] = newOpType; + return true; + } + return false; + } + + public String toString(CodeAnalyzer ca, String[] operands) { + return getOperatorString() + operands[0]; + } +} diff --git a/jode/jode/type/UnknownSubType.java b/jode/jode/type/UnknownSubType.java new file mode 100644 index 0000000..52120a5 --- /dev/null +++ b/jode/jode/type/UnknownSubType.java @@ -0,0 +1,22 @@ +package jode; +import sun.tools.java.Type; + +public class UnknownSubType extends UnknownType { + Type elemType; + + public UnknownSubType(Type type) { + super(103, "<"); + elemType = type; + } + + public Type getElementType() + { + return elemType; + } + + public String typeString(String string, boolean flag1, boolean flag2) + { + return ""; + } +} diff --git a/jode/jode/type/UnknownType.java b/jode/jode/type/UnknownType.java new file mode 100644 index 0000000..a964699 --- /dev/null +++ b/jode/jode/type/UnknownType.java @@ -0,0 +1,204 @@ +package jode; +import sun.tools.java.Type; +import java.util.Hashtable; + +public class UnknownType extends Type { + static Hashtable subclasses = new Hashtable(); + + public static Type tUnknown = new UnknownType(100, "x"); + public static Type tUInt = new UnknownType(101, "i"); + public static Type tUIndex = new UnknownType(104, "["); + public static Type tUObject = new UnknownType(102, "*"); + public static Type tSubClass(Type type) { + Type subtype = (Type) subclasses.get(type); + if (subtype == null) { + subtype = new UnknownSubType(type); + subclasses.put(type, subtype); + } + return subtype; + } + + protected UnknownType(int i, String str) { + super (i, str); + } + + public int stackSize() + { + return 1; + } + + public String typeString(String var, boolean flag1, boolean flag2) + { + String typeStr; + switch (typeCode) { + case 100: typeStr=""; break; + case 101: typeStr=""; break; /*XXX*/ + case 102: typeStr=""; break; + case 104: typeStr=""; break; /*XXX*/ + default: + throw new RuntimeException("Wrong typeCode "+typeCode); + } + if (var.length() > 0) + return typeStr+" "+var; + return typeStr; + } + + public static Type commonType(Type t1, Type t2) { + if (t1 == t2 || t2 == tUnknown) + return t1; + + switch (t1.getTypeCode()) { + case 0: /* boolean*/ + case 1: /* byte */ + case 2: /* char */ + case 3: /* short */ + case 4: /* int */ + if (t2.getTypeCode() <= 4) { + if (t2.getTypeCode() > t1.getTypeCode()) + return t2; + else + return t1; + } + if (t2 == tUInt || t2 == tUIndex) + return t1; + break; + + case 5: /* long */ + case 6: /* float */ + case 7: /* double */ + case 8: /* null? */ + case 11: /* void */ + case 12: /* method */ + case 13: /* error */ + break; + + case 9: /* array */ + if (t2 == tUObject) + return t1; + if (t2.getTypeCode() == 9) /* array, array case */ + return tArray(commonType(t1.getElementType(), + t2.getElementType())); + break; + + case 10: /* class */ + if (t2 == tUObject) + return t1; + if (t2.getTypeCode() == 103) { + /* find suitable subclass of t2 */ + return t2; /*XXX*/ + } + if (t2.getTypeCode() == 10) { + return t1; /*XXX*/ + } + break; + + case 100: /* unknown */ + return t2; + + case 101: /* unknown int */ + if ((t2.getTypeCode() >= 0 && t2.getTypeCode() <= 4) || + t2 == tUIndex) + return t2; + break; + case 104: /* unknown index */ + if (t2.getTypeCode() >= 1 && t2.getTypeCode() <= 4) + return t2; + if (t2 == tUInt) + return t1; + break; + + case 102: /* unknown object */ + if (t2.getTypeCode() == 9 || t2.getTypeCode() == 10 || + t2.getTypeCode() == 103) + return t2; + break; + + case 103: /* unknown super class */ + if (t2.getTypeCode() == 10 || t2.getTypeCode() == 103) + return t2; /*XXX*/ + if (t2 == tUObject) + return t1; + break; + + default: + throw new AssertError("Wrong typeCode "+t1.getTypeCode()); + } + return tError; + } + + /** + * Check if t1 is a t2. + * @return true if t1 is a more specific type than t2, e.g. + * if t2 is a superclass of t1 + */ + public static boolean isOfType(Type t1, Type t2) { + if ((t1 == t2 || t2 == tUnknown) && t1 != tError) + return true; + + switch (t1.getTypeCode()) { + case 0: /* boolean*/ + case 1: /* byte */ + case 2: /* char */ + case 3: /* short */ + case 4: /* int */ + + /* JavaC thinks, that this is okay. */ + if (t2.getTypeCode() >= 0 && t2.getTypeCode() <=4) + return true; + + /* fallthrough */ + case 104: /* unknown index */ + if (t2 == tUInt || t2 == tUIndex) + return true; + break; + + case 5: /* long */ + case 6: /* float */ + case 7: /* double */ + case 8: /* null? */ + case 11: /* void */ + case 12: /* method */ + case 13: /* error */ + case 100: /* unknown */ + case 101: /* unknown int */ + case 102: /* unknown object */ + break; + + case 9: /* array */ + if (t2 == tUObject) + return true; + if (t2.getTypeCode() == 9) /* array,array case */ + return isOfType(t1.getElementType(), t2.getElementType()); + break; + + case 10: /* class */ + if (t2 == tUObject) + return true; + if (t2.getTypeCode() == 103) + /* Always true because t2 may be an Object XXX I think not*/ + return true; + if (t2.getTypeCode() == 10) + /* true if t2 is a superclass of t1 */ + return true; /*XXX*/ + break; + + case 103: /* unknown super class */ + if (t2.getTypeCode() == 103) + /* Always true because t2 may be an Object XXX I think not*/ + return true; + + if (t2.getTypeCode() == 10) { + /* true if t2 is a real super class + (or interface) of t1.getElementType() */ + return true; /*XXX*/ + } + if (t2 == tUObject) + return true; + break; + + default: + throw new AssertError("Wrong typeCode "+t1.getTypeCode()); + } + return false; + } +}