From 723088e8bea8db527dce00a9601e84160b11c70f Mon Sep 17 00:00:00 2001 From: hoenicke Date: Mon, 3 Jul 2000 12:48:10 +0000 Subject: [PATCH] New bytecode interface: - Cleaned up loading of class files (via ClassPath). - load/read can throw IOException - no implicit loading of information on demand (which never completely worked) - more documentation. - BasicBlock representation of method code. git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1244 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/bytecode/BasicBlockReader.java | 960 +++++++++++ jode/jode/bytecode/BasicBlockWriter.java | 983 +++++++++++ jode/jode/bytecode/BasicBlocks.java | 318 ++++ .../{BinaryInfo.java.in => BinaryInfo.java} | 53 +- jode/jode/bytecode/Block.java | 212 +++ jode/jode/bytecode/BytecodeInfo.java.in | 1509 ----------------- jode/jode/bytecode/ClassFormatException.java | 12 +- jode/jode/bytecode/ClassInfo.java | 1469 ++++++++++++++++ jode/jode/bytecode/ClassInfo.java.in | 868 ---------- jode/jode/bytecode/ClassPath.java | 829 +++++++++ jode/jode/bytecode/ConstantInstruction.java | 64 + jode/jode/bytecode/ConstantPool.java | 2 +- jode/jode/bytecode/FieldInfo.java | 47 +- jode/jode/bytecode/GrowableConstantPool.java | 4 +- jode/jode/bytecode/Handler.java | 71 +- jode/jode/bytecode/IncInstruction.java | 70 + jode/jode/bytecode/InnerClassInfo.java | 42 - jode/jode/bytecode/Instruction.java | 833 ++------- jode/jode/bytecode/LineNumber.java | 29 - jode/jode/bytecode/LocalVariableInfo.java | 81 +- jode/jode/bytecode/Makefile.am | 17 +- jode/jode/bytecode/MethodInfo.java | 125 +- jode/jode/bytecode/Opcodes.java | 10 + .../{Reference.java.in => Reference.java} | 10 +- jode/jode/bytecode/ReferenceInstruction.java | 101 ++ jode/jode/bytecode/SearchPath.java | 576 ------- jode/jode/bytecode/SlotInstruction.java | 94 + jode/jode/bytecode/SwitchInstruction.java | 67 + .../bytecode/TypeDimensionInstruction.java | 86 + jode/jode/bytecode/TypeInstruction.java | 64 + jode/jode/bytecode/TypeSignature.java | 5 +- 31 files changed, 5774 insertions(+), 3837 deletions(-) create mode 100644 jode/jode/bytecode/BasicBlockReader.java create mode 100644 jode/jode/bytecode/BasicBlockWriter.java create mode 100644 jode/jode/bytecode/BasicBlocks.java rename jode/jode/bytecode/{BinaryInfo.java.in => BinaryInfo.java} (77%) create mode 100644 jode/jode/bytecode/Block.java delete mode 100644 jode/jode/bytecode/BytecodeInfo.java.in create mode 100644 jode/jode/bytecode/ClassInfo.java delete mode 100644 jode/jode/bytecode/ClassInfo.java.in create mode 100644 jode/jode/bytecode/ClassPath.java create mode 100644 jode/jode/bytecode/ConstantInstruction.java create mode 100644 jode/jode/bytecode/IncInstruction.java delete mode 100644 jode/jode/bytecode/InnerClassInfo.java delete mode 100644 jode/jode/bytecode/LineNumber.java rename jode/jode/bytecode/{Reference.java.in => Reference.java} (88%) create mode 100644 jode/jode/bytecode/ReferenceInstruction.java delete mode 100644 jode/jode/bytecode/SearchPath.java create mode 100644 jode/jode/bytecode/SlotInstruction.java create mode 100644 jode/jode/bytecode/SwitchInstruction.java create mode 100644 jode/jode/bytecode/TypeDimensionInstruction.java create mode 100644 jode/jode/bytecode/TypeInstruction.java diff --git a/jode/jode/bytecode/BasicBlockReader.java b/jode/jode/bytecode/BasicBlockReader.java new file mode 100644 index 0000000..8248500 --- /dev/null +++ b/jode/jode/bytecode/BasicBlockReader.java @@ -0,0 +1,960 @@ +/* BasicBlockReader Copyright (C) 1999-20000 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +import jode.GlobalOptions; +import java.io.DataInputStream; +import java.io.IOException; +///#def COLLECTIONS java.util +import java.util.Arrays; +import java.util.Stack; +import java.util.Vector; +import java.util.Enumeration; +///#enddef + +/** + * This is a helper class, that contains the method to read in basic + * blocks from class files and write them back again. + */ +class BasicBlockReader implements Opcodes { + + static final int IS_BORDER = 1; + static final int IS_REACHABLE = 2; + static final int IS_FORWARD = 4; + static final int IS_CATCHER = 8; + + private class InstrInfo { + Instruction instr; + int stack; + int flags; + int addr; + int nextAddr; + int blockNr; + int[] succs; + + InstrInfo () { + blockNr = stack = addr = nextAddr = -1; + } + } + + private class HandlerEntry { + int start, end, catcher; + String type; + } + + private class LVTEntry { + int local; + int start, end; + String name, type; + } + + private class LNTEntry { + int lineNr; + int start; + } + + InstrInfo[] infos; + HandlerEntry[] handlers; + BasicBlocks bb; + Block[] blocks; + + public BasicBlockReader(BasicBlocks bb) { + this.bb = bb; + } + + private void markReachableBlocks() throws ClassFormatException { + Stack todo = new Stack(); + todo.push(infos[0]); + infos[0].flags = IS_REACHABLE; + infos[0].stack = 0; + + int[] poppush = new int[2]; + /* Iterate until no more reachable instructions are found */ + while (!todo.isEmpty()) { + InstrInfo info = (InstrInfo) todo.pop(); + int stack = info.stack; + if ((info.flags & IS_CATCHER) == 0 + && (info.instr.getOpcode() == opc_goto + || (info.instr.getOpcode() == opc_return + && info.stack == 0))) { + /* This is a forward block. + */ + info.flags |= IS_FORWARD; + } else { + // Check for reachable exception handlers + for (int i=0; i < handlers.length; i++) { + if (handlers[i].start <= info.addr + && handlers[i].end > info.addr) { + InstrInfo catcher = infos[handlers[i].catcher]; + if ((catcher.flags & IS_REACHABLE) == 0) { + catcher.flags |= IS_REACHABLE; + catcher.stack = 1; + todo.push(catcher); + } + } + } + + InstrInfo prevInfo = null; + // Search the end of the block and calculate next stack depth + while (true) { + info.instr.getStackPopPush(poppush); + stack += poppush[1] - poppush[0]; + if (stack < 0) { + throw new ClassFormatException + ("Pop from empty stack: " + bb); + } + + if (!info.instr.doesAlwaysJump() + && info.succs == null + && (infos[info.nextAddr].flags & IS_BORDER) == 0) { + prevInfo = info; + try { + info = infos[info.nextAddr]; + } catch (ArrayIndexOutOfBoundsException ex) { + throw new ClassFormatException + ("Flow falls out of method " + bb); + } + } else + break; + } + + if (info.instr.getOpcode() == opc_goto + || (info.instr.getOpcode() == opc_return + && stack == 0)) { + /* If list is a goto or return, we step an instruction + * back. We don't need to modify stack, since goto and + * return are neutral. + */ + info = prevInfo; + } + } + + /* mark successors as reachable */ + int[] succs = info.succs; + if (succs != null) { + for (int i=0; i < succs.length; i++) { + InstrInfo succ = infos[succs[i]]; + if ((succ.flags & IS_REACHABLE) == 0) { + succ.flags |= IS_REACHABLE; + int succstack = stack; + if (info.instr.getOpcode() == opc_jsr) + succstack++; + if (succ.stack < 0) + succ.stack = succstack; + else if (succ.stack != succstack) + throw new ClassFormatException + ("Stack height varies: "+bb+":"+succs[i]); + todo.push(succ); + } + } + } + if (info.nextAddr < infos.length) + infos[info.nextAddr].flags |= IS_BORDER; + + if (!info.instr.doesAlwaysJump()) { + InstrInfo succ = infos[info.nextAddr]; + if ((succ.flags & IS_REACHABLE) == 0) { + succ.flags |= IS_REACHABLE; + if (succ.stack < 0) + succ.stack = stack; + else if (succ.stack != stack) + throw new ClassFormatException + ("Stack height varies: "+bb+":"+info.nextAddr); + todo.push(succ); + } + } + } + } + + private int getSuccBlockNr(int succAddr) { + InstrInfo succ = infos[succAddr]; + while ((succ.flags & IS_FORWARD) != 0) { + switch (succ.instr.getOpcode()) { + case opc_goto: + succ = infos[succ.succs[0]]; + break; + case opc_return: + return -1; + default: + throw new IllegalStateException(); + } + } + return succ.blockNr; + } + + private Block getSuccBlock(int succAddr) { + int nr = getSuccBlockNr(succAddr); + return nr == -1 ? null : blocks[nr]; + } + + + private void convertHandlers() { + int newCount = 0; + for (int i=0; i < handlers.length; i++) { + if ((infos[handlers[i].catcher].flags & IS_REACHABLE) != 0) + newCount++; + } + Handler[] newHandlers = new Handler[newCount]; + int ptr = 0; + for (int i=0; i= maxLocals) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction(wideopcode, slot); + length = 4; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print + (" " + opcodeString[wideopcode] + " " + slot); + break; + } + case opc_lload: case opc_dload: + case opc_lstore: case opc_dstore: { + int slot = input.readUnsignedShort(); + if (slot >= maxLocals-1) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction(wideopcode, slot); + length = 4; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print + (" " + opcodeString[wideopcode] + " " + slot); + break; + } + case opc_ret: { + int slot = input.readUnsignedShort(); + if (slot >= maxLocals) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction(wideopcode, slot); + length = 4; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" ret "+slot); + break; + } + case opc_iinc: { + int slot = input.readUnsignedShort(); + if (slot >= maxLocals) + throw new ClassFormatException + ("Invalid local slot "+slot); + int incr = input.readShort(); + instr = new IncInstruction(wideopcode, slot, incr); + length = 6; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print + (" iinc " + slot + " " + instr.getIncrement()); + break; + } + default: + throw new ClassFormatException("Invalid wide opcode " + +wideopcode); + } + break; + } + case opc_iload_0: case opc_iload_1: + case opc_iload_2: case opc_iload_3: + case opc_lload_0: case opc_lload_1: + case opc_lload_2: case opc_lload_3: + case opc_fload_0: case opc_fload_1: + case opc_fload_2: case opc_fload_3: + case opc_dload_0: case opc_dload_1: + case opc_dload_2: case opc_dload_3: + case opc_aload_0: case opc_aload_1: + case opc_aload_2: case opc_aload_3: { + int slot = (opcode-opc_lload_0) & 3; + if (slot >= maxLocals) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction + (opc_iload + (opcode-opc_iload_0)/4, slot); + length = 1; + break; + } + case opc_istore_0: case opc_istore_1: + case opc_istore_2: case opc_istore_3: + case opc_fstore_0: case opc_fstore_1: + case opc_fstore_2: case opc_fstore_3: + case opc_astore_0: case opc_astore_1: + case opc_astore_2: case opc_astore_3: { + int slot = (opcode-opc_istore_0) & 3; + if (slot >= maxLocals) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction + (opc_istore + (opcode-opc_istore_0)/4, slot); + length = 1; + break; + } + case opc_lstore_0: case opc_lstore_1: + case opc_lstore_2: case opc_lstore_3: + case opc_dstore_0: case opc_dstore_1: + case opc_dstore_2: case opc_dstore_3: { + int slot = (opcode-opc_lstore_0) & 3; + if (slot >= maxLocals-1) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction + (opc_lstore + (opcode-opc_lstore_0)/4, slot); + length = 1; + break; + } + case opc_iload: case opc_fload: case opc_aload: + case opc_istore: case opc_fstore: case opc_astore: { + int slot = input.readUnsignedByte(); + if (slot >= maxLocals) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction(opcode, slot); + length = 2; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+slot); + break; + } + case opc_lstore: case opc_dstore: + case opc_lload: case opc_dload: { + int slot = input.readUnsignedByte(); + if (slot >= maxLocals - 1) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction(opcode, slot); + length = 2; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+slot); + break; + } + case opc_ret: { + int slot = input.readUnsignedByte(); + if (slot >= maxLocals) + throw new ClassFormatException + ("Invalid local slot "+slot); + instr = new SlotInstruction(opcode, slot); + length = 2; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+slot); + break; + } + case opc_aconst_null: + case opc_iconst_m1: + case opc_iconst_0: case opc_iconst_1: case opc_iconst_2: + case opc_iconst_3: case opc_iconst_4: case opc_iconst_5: + case opc_fconst_0: case opc_fconst_1: case opc_fconst_2: + instr = new ConstantInstruction + (opc_ldc, constants[opcode - opc_aconst_null]); + length = 1; + break; + case opc_lconst_0: case opc_lconst_1: + case opc_dconst_0: case opc_dconst_1: + instr = new ConstantInstruction + (opc_ldc2_w, constants[opcode - opc_aconst_null]); + length = 1; + break; + case opc_bipush: + instr = new ConstantInstruction + (opc_ldc, new Integer(input.readByte())); + length = 2; + break; + case opc_sipush: + instr = new ConstantInstruction + (opc_ldc, new Integer(input.readShort())); + length = 3; + break; + case opc_ldc: { + int index = input.readUnsignedByte(); + int tag = cp.getTag(index); + if (tag != cp.STRING + && tag != cp.INTEGER && tag != cp.FLOAT) + throw new ClassFormatException + ("wrong constant tag: "+tag); + instr = new ConstantInstruction + (opc_ldc, cp.getConstant(index)); + length = 2; + break; + } + case opc_ldc_w: { + int index = input.readUnsignedShort(); + int tag = cp.getTag(index); + if (tag != cp.STRING + && tag != cp.INTEGER && tag != cp.FLOAT) + throw new ClassFormatException + ("wrong constant tag: "+tag); + instr = new ConstantInstruction + (opc_ldc, cp.getConstant(index)); + length = 3; + break; + } + case opc_ldc2_w: { + int index = input.readUnsignedShort(); + int tag = cp.getTag(index); + if (tag != cp.LONG && tag != cp.DOUBLE) + throw new ClassFormatException + ("wrong constant tag: "+tag); + instr = new ConstantInstruction + (opc_ldc2_w, cp.getConstant(index)); + length = 3; + break; + } + case opc_iinc: { + int slot = input.readUnsignedByte(); + if (slot >= maxLocals) + throw new ClassFormatException + ("Invalid local slot "+slot); + int incr = input.readByte(); + instr = new IncInstruction(opcode, slot, incr); + length = 3; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print + (" " + slot + " " + instr.getIncrement()); + break; + } + case opc_goto: + case opc_jsr: + case opc_ifeq: case opc_ifne: + case opc_iflt: case opc_ifge: + case opc_ifgt: case opc_ifle: + case opc_if_icmpeq: case opc_if_icmpne: + case opc_if_icmplt: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: + instr = new Instruction(opcode); + length = 3; + infos[addr].succs = new int[] { addr+input.readShort() }; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+infos[addr].succs[0]); + break; + + case opc_goto_w: + case opc_jsr_w: + instr = new Instruction(opcode - (opc_goto_w - opc_goto)); + length = 5; + infos[addr].succs = new int[] { addr+input.readInt() }; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+infos[addr].succs[0]); + break; + + case opc_tableswitch: { + length = 3 - (addr % 4); + input.readFully(new byte[length]); + int def = input.readInt(); + int low = input.readInt(); + int high = input.readInt(); + int[] dests = new int[high-low+1]; + int npairs = 0; + for (int i=0; i < dests.length; i++) { + dests[i] = input.readInt(); + if (dests[i] != def) + npairs++; + } + infos[addr].succs = new int[npairs + 1]; + int[] values = new int[npairs]; + int pos = 0; + for (int i=0; i < dests.length; i++) { + if (dests[i] != def) { + values[pos] = i+low; + infos[addr].succs[pos] = addr + dests[i]; + pos++; + } + } + infos[addr].succs[npairs] = addr + def; + instr = new SwitchInstruction(opc_lookupswitch, values); + length += 13 + 4 * (high-low+1); + break; + } + case opc_lookupswitch: { + length = 3 - (addr % 4); + input.readFully(new byte[length]); + int def = input.readInt(); + int npairs = input.readInt(); + infos[addr].succs = new int[npairs + 1]; + int[] values = new int[npairs]; + for (int i=0; i < npairs; i++) { + values[i] = input.readInt(); + if (i > 0 && values[i-1] >= values[i]) + throw new ClassFormatException + ("lookupswitch not sorted"); + infos[addr].succs[i] = addr + input.readInt(); + } + infos[addr].succs[npairs] = addr + def; + instr = new SwitchInstruction(opc_lookupswitch, values); + length += 9 + 8 * npairs; + break; + } + + case opc_getstatic: + case opc_getfield: + case opc_putstatic: + case opc_putfield: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokevirtual: { + int index = input.readUnsignedShort(); + int tag = cp.getTag(index); + if (opcode < opc_invokevirtual) { + if (tag != cp.FIELDREF) + throw new ClassFormatException + ("field tag mismatch: "+tag); + } else { + if (tag != cp.METHODREF) + throw new ClassFormatException + ("method tag mismatch: "+tag); + } + Reference ref = cp.getRef(index); + if (ref.getName().charAt(0) == '<' + && (!ref.getName().equals("") + || opcode != opc_invokespecial)) + throw new ClassFormatException + ("Illegal call of special method/field "+ref); + instr = new ReferenceInstruction(opcode, ref); + length = 3; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+ref); + break; + } + case opc_invokeinterface: { + int index = input.readUnsignedShort(); + int tag = cp.getTag(index); + if (tag != cp.INTERFACEMETHODREF) + throw new ClassFormatException + ("interface tag mismatch: "+tag); + Reference ref = cp.getRef(index); + if (ref.getName().charAt(0) == '<') + throw new ClassFormatException + ("Illegal call of special method "+ref); + int nargs = input.readUnsignedByte(); + if (TypeSignature.getArgumentSize(ref.getType()) + != nargs - 1) + throw new ClassFormatException + ("Interface nargs mismatch: "+ref+" vs. "+nargs); + if (input.readUnsignedByte() != 0) + throw new ClassFormatException + ("Interface reserved param not zero"); + + instr = new ReferenceInstruction(opcode, ref); + length = 5; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+ref); + break; + } + + case opc_new: + case opc_checkcast: + case opc_instanceof: { + String type = cp.getClassType(input.readUnsignedShort()); + if (opcode == opc_new && type.charAt(0) == '[') + throw new ClassFormatException + ("Can't create array with opc_new"); + instr = new TypeInstruction(opcode, type); + length = 3; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+type); + break; + } + case opc_multianewarray: { + String type = cp.getClassType(input.readUnsignedShort()); + int dims = input.readUnsignedByte(); + if (dims == 0) + throw new ClassFormatException + ("multianewarray dimension is 0."); + for (int i=0; i < dims; i++) { + /* Note that since type is a valid type + * signature, there must be a non bracket + * character, before the string is over. + * So there is no StringIndexOutOfBoundsException. + */ + if (type.charAt(i) != '[') + throw new ClassFormatException + ("multianewarray called for non array:"+ type); + } + instr = new TypeDimensionInstruction(opcode, type, dims); + length = 4; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" " + type + " " + dims); + break; + } + case opc_anewarray: { + String type + = "["+cp.getClassType(input.readUnsignedShort()); + instr = new TypeDimensionInstruction + (opc_multianewarray, type.intern(), 1); + length = 3; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+type); + break; + } + case opc_newarray: { + char sig = newArrayTypes.charAt + (input.readUnsignedByte()-4); + String type = new String (new char[] { '[', sig }); + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.print(" "+type); + instr = new TypeDimensionInstruction + (opc_multianewarray, type.intern(), 1); + length = 2; + break; + } + + case opc_nop: + case opc_iaload: case opc_laload: case opc_faload: + case opc_daload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: + case opc_iastore: case opc_lastore: case opc_fastore: + case opc_dastore: case opc_aastore: + case opc_bastore: case opc_castore: case opc_sastore: + case opc_pop: case opc_pop2: + case opc_dup: case opc_dup_x1: case opc_dup_x2: + case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: + case opc_swap: + case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: + case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: + case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: + case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: + case opc_irem: case opc_lrem: case opc_frem: case opc_drem: + case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: + case opc_ishl: case opc_lshl: + case opc_ishr: case opc_lshr: + case opc_iushr: case opc_lushr: + case opc_iand: case opc_land: + case opc_ior: case opc_lor: + case opc_ixor: case opc_lxor: + case opc_i2l: case opc_i2f: case opc_i2d: + case opc_l2i: case opc_l2f: case opc_l2d: + case opc_f2i: case opc_f2l: case opc_f2d: + case opc_d2i: case opc_d2l: case opc_d2f: + case opc_i2b: case opc_i2c: case opc_i2s: + case opc_lcmp: case opc_fcmpl: case opc_fcmpg: + case opc_dcmpl: case opc_dcmpg: + case opc_ireturn: case opc_lreturn: + case opc_freturn: case opc_dreturn: case opc_areturn: + case opc_return: + case opc_athrow: + case opc_arraylength: + case opc_monitorenter: case opc_monitorexit: + instr = new Instruction(opcode); + length = 1; + break; + default: + throw new ClassFormatException("Invalid opcode "+opcode); + } + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.println(); + + infos[addr].instr = instr; + infos[addr].addr = addr; + infos[addr].nextAddr = addr + length; + if (addr + length == codeLength && !instr.doesAlwaysJump()) + throw new ClassFormatException + ("Flow falls out of method " + bb); + addr += length; + } + if (addr != codeLength) + throw new ClassFormatException("last instruction too long"); + } + + int handlersLength = input.readUnsignedShort(); + handlers = new HandlerEntry[handlersLength]; + for (int i=0; i< handlersLength; i ++) { + handlers[i] = new HandlerEntry(); + handlers[i].start = input.readUnsignedShort(); + handlers[i].end = input.readUnsignedShort(); + handlers[i].catcher = input.readUnsignedShort(); + int index = input.readUnsignedShort(); + handlers[i].type = (index == 0) ? null + : cp.getClassName(index); + } + + for (int i=0; i< infos.length; i++) { + if (infos[i] != null && infos[i].succs != null) { + int[] succs = infos[i].succs; + for (int j=0; j < succs.length; j++) { + try { + infos[succs[j]].flags |= IS_BORDER; + } catch (RuntimeException ex) { + throw new ClassFormatException + ("Illegal successor: " + bb+":"+i); + } + } + } + } + + for (int i=0; i< handlersLength; i ++) { + /* Mark the instructions as border instructions. + * reachable. + */ + infos[handlers[i].start].flags |= IS_BORDER; + if (handlers[i].end < infos.length) + infos[handlers[i].end].flags |= IS_BORDER; + infos[handlers[i].catcher].flags |= IS_BORDER | IS_CATCHER; + } + } + + public void readLVT(int length, ConstantPool cp, + DataInputStream input) throws IOException { + if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0) + GlobalOptions.err.println("LocalVariableTable of "+bb); + int count = input.readUnsignedShort(); + if (length != 2 + count * 10) { + if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0) + GlobalOptions.err.println("Illegal LVT length, ignoring it"); + return; + } + Vector[] lvt = new Vector[bb.maxLocals]; + for (int i=0; i < count; i++) { + LVTEntry lve = new LVTEntry(); + lve.start = input.readUnsignedShort(); + lve.end = lve.start + input.readUnsignedShort(); + int slot = input.readUnsignedShort(); + int nameIndex = input.readUnsignedShort(); + int typeIndex = input.readUnsignedShort(); + if (cp.getTag(nameIndex) != cp.UTF8 + || cp.getTag(typeIndex) != cp.UTF8) { + + // This is probably an evil lvt as created by HashJava + // simply ignore it. + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_LVT) != 0) + GlobalOptions.err.println + ("Illegal entry, ignoring LVT"); + lvt = null; + return; + } + lve.name = cp.getUTF8(nameIndex); + lve.type = cp.getUTF8(typeIndex); + if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0) + GlobalOptions.err.println("\t" + lve.name + ": " + + lve.type + +" range "+lve.start + +" - "+lve.end + +" slot "+slot); + if (lvt[slot] == null) + lvt[slot] = new Vector(); + lvt[slot].addElement(lve); + } + for (int i = 0; i< infos.length; i = infos[i].nextAddr) { + Instruction instr = infos[i].instr; + if (instr.hasLocal()) { + LocalVariableInfo lvi = instr.getLocalInfo(); + int slot = lvi.getSlot(); + if (lvt[slot] == null) + continue; + int addr = i; + if (instr.getOpcode() >= opc_istore + && instr.getOpcode() <= opc_astore) + addr = infos[i].nextAddr; + + Enumeration enum = lvt[slot].elements(); + LVTEntry match = null; + while (enum.hasMoreElements()) { + LVTEntry lve = (LVTEntry) enum.nextElement(); + if (lve.start <= addr && lve.end > addr) { + if (match != null + && (!match.name.equals(lve.name) + || !match.type.equals(lve.type))) { + /* Multiple matches..., give no info */ + match = null; + break; + } + match = lve; + } + } + if (match != null) + instr.setLocalInfo(LocalVariableInfo + .getInfo(slot, match.name, match.type)); + } + } + + int paramCount = bb.getParamCount(); + for (int slot=0; slot< paramCount; slot++) { + if (lvt[slot] == null) + continue; + Enumeration enum = lvt[slot].elements(); + LVTEntry match = null; + while (enum.hasMoreElements()) { + LVTEntry lve = (LVTEntry) enum.nextElement(); + if (lve.start == 0) { + if (match != null + && (!match.name.equals(lve.name) + || !match.type.equals(lve.type))) { + /* Multiple matches..., give no info */ + match = null; + break; + } + match = lve; + } + } + if (match != null) { + bb.setParamInfo(LocalVariableInfo + .getInfo(slot, match.name, match.type)); + } + } + } + + public void readLNT(int length, ConstantPool cp, + DataInputStream input) throws IOException { + int count = input.readUnsignedShort(); + if (length != 2 + count * 4) { + GlobalOptions.err.println + ("Illegal LineNumberTable, ignoring it"); + return; + } + for (int i = 0; i < count; i++) { + int start = input.readUnsignedShort(); + infos[start].instr.setLineNr(input.readUnsignedShort()); + } + + int lastLine = -1; + for (int i = 0; i< infos.length; i = infos[i].nextAddr) { + Instruction instr = infos[i].instr; + if (instr.hasLineNr()) + lastLine = instr.getLineNr(); + else + instr.setLineNr(lastLine); + } + } +} diff --git a/jode/jode/bytecode/BasicBlockWriter.java b/jode/jode/bytecode/BasicBlockWriter.java new file mode 100644 index 0000000..5f3198e --- /dev/null +++ b/jode/jode/bytecode/BasicBlockWriter.java @@ -0,0 +1,983 @@ +/* BasicBlockWriter Copyright (C) 1999-20000 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.BitSet; +///#def COLLECTIONS java.util +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Stack; +///#enddef + +/** + * This is a helper class, that contains the method to write basic + * blocks to a class file. + */ +class BasicBlockWriter implements Opcodes { + + private class LVTEntry { + int startAddr, endAddr; + Instruction start, end; + LocalVariableInfo lvi; + } + + BasicBlocks bb; + int[] blockAddr; + int[][] instrLength; + + int lntCount; + short[] lnt; + LVTEntry[] lvt; + + boolean retAtEnd; + int lastRetAddr; + + BitSet isRet; + BitSet isWide; + BitSet isWideCond; + + public BasicBlockWriter(BasicBlocks bb, GrowableConstantPool gcp) { + this.bb = bb; + init(gcp); + prepare(gcp); + } + + public void buildNewLVT() { + Block startBlock = bb.getStartBlock(); + Block[] blocks = bb.getBlocks(); + if (startBlock == null) + return; + + /* We begin with the first Instruction and follow program flow. + * We remember which locals are life at start of each block + * in atStart. + */ + LocalVariableInfo[][] atStart = + new LocalVariableInfo[blocks.length][]; + int startBlockNr = startBlock.getBlockNr(); + atStart[startBlockNr] = new LocalVariableInfo[bb.getMaxLocals()]; + for (int i=0; i < bb.getParamCount(); i++) + atStart[startBlockNr][i] = bb.getParamInfo(i); + + /* We currently ignore the jsr/ret issue. Should be okay, + * though, since it can only generate a bit too much local + * information. */ + Stack todo = new Stack(); + todo.push(startBlock); + while (!todo.isEmpty()) { + Block block = (Block) todo.pop(); + int blockNr = block.getBlockNr(); + LocalVariableInfo[] life + = (LocalVariableInfo[]) atStart[blockNr].clone(); + for (Iterator iter = block.getInstructions().iterator(); + iter.hasNext() ; ) { + Instruction instr = (Instruction) iter.next(); + if (instr.hasLocal()) { + LocalVariableInfo lvi = instr.getLocalInfo(); + int slot = lvi.getSlot(); + if (life[slot] != null + && life[slot] != lvi) + life[slot] = null; + + if (life[slot] == null + && lvi.getName() != null) + life[slot] = lvi; + } + } + Block[] succs = block.getSuccs(); + if (succs != null) { + for (int j = 0; j < succs.length; j++) { + if (succs[j] == null) + continue; + int succNr = succs[j].getBlockNr(); + if (atStart[succNr] == null) { + atStart[succNr] = (LocalVariableInfo[]) life.clone(); + todo.push(succs[j]); + } else { + boolean changed = false; + for (int k = 0; k < life.length; k++) { + if (atStart[succNr][k] != life[k] + && atStart[succNr][k] != null) { + atStart[succNr][k] = null; + changed = true; + } + } + if (changed && !todo.contains(succs[j])) + todo.push(succs[j]); + } + } + } + } + + ArrayList lvtEntries = new ArrayList(); + + LVTEntry[] current = new LVTEntry[bb.getMaxLocals()]; + for (int slot=0; slot < bb.getParamCount(); slot++) { + LocalVariableInfo lvi = bb.getParamInfo(slot); + if (lvi.getName() != null) { + current[slot] = new LVTEntry(); + current[slot].startAddr = 0; + current[slot].lvi = lvi; + } + } + + for (int i=0; i < blocks.length; i++) { + if (atStart[i] == null) + // ignore unreachable blocks: + continue; + + Block block = blocks[i]; + int addr = blockAddr[i]; + for (int slot = 0; slot < current.length; slot++) { + if (current[slot] != null + && current[slot].lvi != atStart[i][slot]) { + current[slot].endAddr = addr; + lvtEntries.add(current[slot]); + current[slot] = null; + } + if (current[slot] == null && atStart[i][slot] != null) { + current[slot] = new LVTEntry(); + current[slot].startAddr = addr; + current[slot].lvi = atStart[i][slot]; + } + } + + int size = block.getInstructions().size(); + for (int k = 0; k < size; k++) { + Instruction instr + = (Instruction) block.getInstructions().get(k); + if (instr.hasLocal()) { + LocalVariableInfo lvi = instr.getLocalInfo(); + int slot = lvi.getSlot(); + if (current[slot] != null + && current[slot].lvi != lvi) { + current[slot].endAddr = addr; + lvtEntries.add(current[slot]); + current[slot] = null; + } + if (current[slot] == null + && lvi.getName() != null) { + current[slot] = new LVTEntry(); + current[slot].startAddr = addr; + current[slot].lvi = lvi; + } + } + addr += instrLength[i][k]; + } + } + + for (int slot = 0; slot < current.length; slot++) { + if (current[slot] != null) { + current[slot].endAddr = blockAddr[blockAddr.length - 1]; + lvtEntries.add(current[slot]); + current[slot] = null; + } + } + if (lvtEntries.size() > 0) + lvt = (LVTEntry[]) lvtEntries.toArray + (new LVTEntry[lvtEntries.size()]); + } + + public void init(GrowableConstantPool gcp) { + Block[] blocks = bb.getBlocks(); + blockAddr = new int[blocks.length + 1]; + instrLength = new int[blocks.length + 1][]; + + int[] gotos = new int[blocks.length + 1]; + int[] conds = new int[blocks.length + 1]; + + boolean needRet = false; + boolean hasRet = false; + isRet = new BitSet(); + BitSet isJsr = new BitSet(); + + int addr = 0; + Block startBlock = bb.getStartBlock(); + if (startBlock == null) { + addr++; + isRet.set(0); + hasRet = true; + gotos[0] = -1; + } else if (startBlock != blocks[0]) { + /* reserve 3 byte for a goto at the beginning */ + addr += 3; + gotos[0] = startBlock.getBlockNr(); + } + + next_block: + for (int i = 0; i < blocks.length; i++) { + blockAddr[i] = addr; + List instructions = blocks[i].getInstructions(); + int size = instructions.size(); + instrLength[i] = new int[size]; + Block[] succs = blocks[i].getSuccs(); + for (int j = 0; j < size; j++) { + Instruction instr = (Instruction) instructions.get(j); + if (instr.hasLineNr()) + lntCount++; + + conds[i+1] = -2; + int opcode = instr.getOpcode(); + int length; + switch_opc: + switch (opcode) { + case opc_ldc: + case opc_ldc2_w: { + Object constant = instr.getConstant(); + if (constant == null) { + length = 1; + break switch_opc; + } + for (int k = 1; k < constants.length; k++) { + if (constant.equals(constants[k])) { + length = 1; + break switch_opc; + } + } + if (opcode == opc_ldc2_w) { + gcp.putLongConstant(constant); + length = 3; + break switch_opc; + } + if (constant instanceof Integer) { + int value = ((Integer) constant).intValue(); + if (value >= Byte.MIN_VALUE + && value <= Byte.MAX_VALUE) { + length = 2; + break switch_opc; + } else if (value >= Short.MIN_VALUE + && value <= Short.MAX_VALUE) { + length = 3; + break switch_opc; + } + } + if (gcp.putConstant(constant) < 256) { + length = 2; + } else { + length = 3; + } + break; + } + case opc_iinc: { + int slot = instr.getLocalSlot(); + int increment = instr.getIncrement(); + if (slot < 256 + && increment >= Byte.MIN_VALUE + && increment <= Byte.MAX_VALUE) + length = 3; + else + length = 6; + break; + } + case opc_iload: case opc_lload: + case opc_fload: case opc_dload: case opc_aload: + case opc_istore: case opc_lstore: + case opc_fstore: case opc_dstore: case opc_astore: + if (instr.getLocalSlot() < 4) { + length = 1; + break; + } + if (instr.getLocalSlot() < 256) + length = 2; + else + length = 4; + break; + case opc_ret: { + if (instr.getLocalSlot() < 256) + length = 2; + else + length = 4; + gotos[i+1] = -2; + break; + } + case opc_lookupswitch: { + length = 3-(addr % 4); + int[] values = instr.getValues(); + int npairs = values.length; + for (int k=0; k< succs.length; k++) { + if (succs[k] == null) + needRet = true; + } + if (npairs > 0) { + int tablesize = values[npairs-1] - values[0] + 1; + if (4 + tablesize * 4 < 8 * npairs) { + // Use a table switch + length += 13 + 4 * tablesize; + break; + } + } + // Use a lookup switch + length += 9 + 8 * npairs; + // The goto is inclusive through the default part. + gotos[i+1] = -2; + continue next_block; + } + case opc_jsr: + conds[i+1] = succs[0].getBlockNr(); + length = 3; + isJsr.set(i+1); + break; + case opc_ifeq: case opc_ifne: + case opc_iflt: case opc_ifge: + case opc_ifgt: case opc_ifle: + case opc_if_icmpeq: case opc_if_icmpne: + case opc_if_icmplt: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: + if (succs[0] == null) { + needRet = true; + conds[i+1] = -1; + } else + conds[i+1] = succs[0].getBlockNr(); + length = 3; + break; + case opc_multianewarray: { + if (instr.getDimensions() == 1) { + String clazz = instr.getClazzType().substring(1); + if (newArrayTypes.indexOf(clazz.charAt(0)) != -1) { + length = 2; + } else { + gcp.putClassType(clazz); + length = 3; + } + } else { + gcp.putClassType(instr.getClazzType()); + length = 4; + } + break; + } + case opc_getstatic: + case opc_getfield: + case opc_putstatic: + case opc_putfield: + gcp.putRef(gcp.FIELDREF, instr.getReference()); + length = 3; + break; + case opc_invokespecial: + case opc_invokestatic: + case opc_invokevirtual: + gcp.putRef(gcp.METHODREF, instr.getReference()); + length = 3; + break; + case opc_invokeinterface: + gcp.putRef(gcp.INTERFACEMETHODREF, instr.getReference()); + length = 5; + break; + case opc_new: + case opc_checkcast: + case opc_instanceof: + gcp.putClassType(instr.getClazzType()); + length = 3; + break; + case opc_ireturn: case opc_lreturn: + case opc_freturn: case opc_dreturn: case opc_areturn: + case opc_return: + case opc_athrow: + gotos[i+1] = -2; + length = 1; + break; + case opc_nop: + case opc_iaload: case opc_laload: case opc_faload: + case opc_daload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: + case opc_iastore: case opc_lastore: case opc_fastore: + case opc_dastore: case opc_aastore: + case opc_bastore: case opc_castore: case opc_sastore: + case opc_pop: case opc_pop2: + case opc_dup: case opc_dup_x1: case opc_dup_x2: + case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: + case opc_swap: + case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: + case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: + case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: + case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: + case opc_irem: case opc_lrem: case opc_frem: case opc_drem: + case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: + case opc_ishl: case opc_lshl: + case opc_ishr: case opc_lshr: + case opc_iushr: case opc_lushr: + case opc_iand: case opc_land: + case opc_ior: case opc_lor: + case opc_ixor: case opc_lxor: + case opc_i2l: case opc_i2f: case opc_i2d: + case opc_l2i: case opc_l2f: case opc_l2d: + case opc_f2i: case opc_f2l: case opc_f2d: + case opc_d2i: case opc_d2l: case opc_d2f: + case opc_i2b: case opc_i2c: case opc_i2s: + case opc_lcmp: case opc_fcmpl: case opc_fcmpg: + case opc_dcmpl: case opc_dcmpg: + case opc_arraylength: + case opc_monitorenter: case opc_monitorexit: + length = 1; + break; + default: + throw new IllegalStateException("Invalid opcode "+opcode); + } + instrLength[i][j] = length; + addr += length; + } + Block defaultSucc = succs[succs.length-1]; + if (defaultSucc == null) { + // This is a return + gotos[i+1] = -1; + isRet.set(i+1); + lastRetAddr = addr; + addr++; + } else if (defaultSucc.getBlockNr() == i + 1) { + // no need for any jump + gotos[i+1] = succs[succs.length-1].getBlockNr(); + } else { + // Reserve space for a normal goto. + gotos[i+1] = succs[succs.length-1].getBlockNr(); + addr += 3; + } + } + if (needRet && !hasRet) { + retAtEnd = true; + lastRetAddr = addr; + addr++; + } + blockAddr[blocks.length] = addr; + + isWide = new BitSet(); + isWideCond = new BitSet(); + // Now check for wide goto/jsr/if, but only if method is big enough + boolean changed = addr > Short.MAX_VALUE; + while (changed) { + changed = false; + for (int i = 0; i < gotos.length; i++) { + int gotoNr = gotos[i]; + int condNr = conds[i]; + if (!isWideCond.get(i) && condNr != -2) { + int from = blockAddr[i] - 3; + if (gotoNr != i + 1) + from -= isRet.get(i) ? 1 : isWide.get(i) ? 5 : 3; + int dist; + if (condNr == -1) { + if (!retAtEnd) { + dist = blockAddr[blockAddr.length-1] - 1 - from; + } else { + for (int j = 0; j < gotos.length; j++) { + if (isRet.get(j)) { + dist = blockAddr[j] - 1 - from; + if (dist >= Short.MIN_VALUE + && dist <= Short.MAX_VALUE) + break; + } + } + throw new InternalError(); + } + } else { + dist = blockAddr[condNr] - from; + } + + if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) { + /* We must do the a wide cond: + * if_!xxx L + * goto_w condNr + * L:goto gotoNr + */ + isWideCond.set(i); + int diff = isJsr.get(i) ? 2 : condNr == -1 ? 1 : 5; + instrLength[i][instrLength[i].length-1] += diff; + for (int j = i; j < blockAddr.length; j++) + blockAddr[j] += diff; + changed = true; + } + } + if (!isWide.get(i) && gotoNr >= 0) { + int dist = blockAddr[gotoNr] - blockAddr[i] + 3; + if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) { + /* wide goto, correct addresses */ + isWide.set(i); + for (int j = i; j < blockAddr.length; j++) + blockAddr[j] += 2; + changed = true; + } + } + } + } + buildNewLVT(); + } + + public int getSize() { + /* maxStack: 2 + * maxLocals: 2 + * code: 4 + codeLength + * exc count: 2 + * exceptions: n * 8 + * attributes: + * lvt_name: 2 + * lvt_length: 4 + * lvt_count: 2 + * lvt_entries: n * 10 + * attributes: + * lnt_name: 2 + * lnt_length: 4 + * lnt_count: 2 + * lnt_entries: n * 4 + */ + int attrsize = 0; + if (lvt != null) + attrsize += 8 + lvt.length * 10; + if (lntCount > 0) + attrsize += 8 + lntCount * 4; + return 10 + + blockAddr[blockAddr.length - 1] + + bb.getExceptionHandlers().length * 8 + + attrsize; + } + + protected int getAttributeCount() { + int count = 0; + if (lvt != null) + count++; + if (lntCount > 0) + count++; + return count; + } + + public void prepare(GrowableConstantPool gcp) { + Handler[] handlers = bb.getExceptionHandlers(); + for (int i = 0; i< handlers.length; i++) { + if (handlers[i].type != null) + gcp.putClassName(handlers[i].type); + } + if (lvt != null) { + gcp.putUTF8("LocalVariableTable"); + int count = lvt.length; + for (int i=0; i < count; i++) { + gcp.putUTF8(lvt[i].lvi.getName()); + gcp.putUTF8(lvt[i].lvi.getType()); + } + } + if (lntCount > 0) + gcp.putUTF8("LineNumberTable"); + } + + public void writeAttributes(GrowableConstantPool gcp, + DataOutputStream output) + throws IOException { + if (lvt != null) { + output.writeShort(gcp.putUTF8("LocalVariableTable")); + int count = lvt.length; + int length = 2 + 10 * count; + output.writeInt(length); + output.writeShort(count); + for (int i=0; i < count; i++) { + output.writeShort(lvt[i].startAddr); + output.writeShort(lvt[i].endAddr); + output.writeShort(gcp.putUTF8(lvt[i].lvi.getName())); + output.writeShort(gcp.putUTF8(lvt[i].lvi.getType())); + output.writeShort(lvt[i].lvi.getSlot()); + } + } + if (lnt != null) { + output.writeShort(gcp.putUTF8("LineNumberTable")); + int count = lnt.length / 2; + int length = 2 + 4 * count; + output.writeInt(length); + output.writeShort(count); + for (int i=0; i < count; i++) { + output.writeShort(lnt[2*i]); + output.writeShort(lnt[2*i+1]); + } + } + } + + public void write(GrowableConstantPool gcp, + DataOutputStream output) throws IOException { + output.writeShort(bb.getMaxStack()); + output.writeShort(bb.getMaxLocals()); + Block[] blocks = bb.getBlocks(); + if (blockAddr[blockAddr.length - 1] > 65535) + throw new ClassFormatError("Method too long"); + output.writeInt(blockAddr[blockAddr.length-1]); + lnt = new short[lntCount*2]; + + int addr = 0; + Block startBlock = bb.getStartBlock(); + if (isRet.get(0)) { + output.writeByte(opc_return); + addr ++; + } else if (isWide.get(0)) { + output.writeByte(opc_goto_w); + output.writeInt(blockAddr[startBlock.getBlockNr()]); + addr += 5; + } else if (startBlock != blocks[0]) { + output.writeByte(opc_goto); + output.writeShort(blockAddr[startBlock.getBlockNr()]); + addr += 3; + } + int lntPtr = 0; + + next_block: + for (int i = 0; i< blocks.length; i++) { + Block[] succs = blocks[i].getSuccs(); + if (addr != blockAddr[i]) + throw new InternalError("Address calculation broken!"); + List instructions = blocks[i].getInstructions(); + int size = instructions.size(); + for (int j = 0; j < size; j++) { + Instruction instr = (Instruction) instructions.get(j); + if (instr.hasLineNr()) { + lnt[lntPtr++] = (short) addr; + lnt[lntPtr++] = (short) instr.getLineNr(); + } + int opcode = instr.getOpcode(); + switch_opc: + switch (opcode) { + case opc_iload: case opc_lload: + case opc_fload: case opc_dload: case opc_aload: + case opc_istore: case opc_lstore: + case opc_fstore: case opc_dstore: case opc_astore: { + int slot = instr.getLocalSlot(); + if (slot < 4) { + if (opcode < opc_istore) + output.writeByte(opc_iload_0 + + 4*(opcode-opc_iload) + + slot); + else + output.writeByte(opc_istore_0 + + 4*(opcode-opc_istore) + + slot); + } else if (slot < 256) { + output.writeByte(opcode); + output.writeByte(slot); + } else { + output.writeByte(opc_wide); + output.writeByte(opcode); + output.writeShort(slot); + } + break; + } + case opc_ret: { + int slot = instr.getLocalSlot(); + if (slot < 256) { + output.writeByte(opcode); + output.writeByte(slot); + } else { + output.writeByte(opc_wide); + output.writeByte(opcode); + output.writeShort(slot); + } + continue next_block; + } + case opc_ldc: + case opc_ldc2_w: { + Object constant = instr.getConstant(); + if (constant == null) { + output.writeByte(opc_aconst_null); + break switch_opc; + } + for (int k = 1; k < constants.length; k++) { + if (constant.equals(constants[k])) { + output.writeByte(opc_aconst_null + k); + break switch_opc; + } + } + if (opcode == opc_ldc2_w) { + output.writeByte(opcode); + output.writeShort(gcp.putLongConstant(constant)); + } else { + if (constant instanceof Integer) { + int value = ((Integer) constant).intValue(); + if (value >= Byte.MIN_VALUE + && value <= Byte.MAX_VALUE) { + + output.writeByte(opc_bipush); + output.writeByte(((Integer)constant) + .intValue()); + break switch_opc; + } else if (value >= Short.MIN_VALUE + && value <= Short.MAX_VALUE) { + output.writeByte(opc_sipush); + output.writeShort(((Integer)constant) + .intValue()); + break switch_opc; + } + } + if (instrLength[i][j] == 2) { + output.writeByte(opc_ldc); + output.writeByte(gcp.putConstant(constant)); + } else { + output.writeByte(opc_ldc_w); + output.writeShort(gcp.putConstant(constant)); + } + } + break; + } + case opc_iinc: { + int slot = instr.getLocalSlot(); + int incr = instr.getIncrement(); + if (instrLength[i][j] == 3) { + output.writeByte(opcode); + output.writeByte(slot); + output.writeByte(incr); + } else { + output.writeByte(opc_wide); + output.writeByte(opcode); + output.writeShort(slot); + output.writeShort(incr); + } + break; + } + case opc_jsr: { + int dist = blockAddr[succs[0].getBlockNr()] - addr; + if (isWideCond.get(i+1)) { + /* wide jsr */ + output.writeByte(opc_jsr_w); + output.writeInt(dist); + } else { + /* wide jsr */ + output.writeByte(opc_jsr); + output.writeShort(dist); + } + break; + } + + case opc_ifeq: case opc_ifne: + case opc_iflt: case opc_ifge: + case opc_ifgt: case opc_ifle: + case opc_if_icmpeq: case opc_if_icmpne: + case opc_if_icmplt: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: + case opc_if_acmpeq: case opc_if_acmpne: + case opc_ifnull: case opc_ifnonnull: { + Block dest = succs[0]; + if (isWideCond.get(i+1)) { + /* swap condition */ + if (opcode >= opc_ifnull) + opcode = opcode ^ 1; + else + opcode = 1 + ((opcode - 1) ^ 1); + output.writeByte(opcode); + if (dest == null) { + output.writeShort(4); + output.writeByte(opc_ret); + } else { + output.writeShort(8); + output.writeByte(opc_goto_w); + int dist = blockAddr[dest.getBlockNr()] - addr; + output.writeInt(dist); + } + } else { + int dist; + if (dest == null) { + if (!retAtEnd) { + dist = blockAddr[blocks.length] - 1 - addr; + } else { + for (int k = 0; k < blocks.length + 1; k++) { + if (isRet.get(k)) { + dist = blockAddr[k] - 1 - addr; + if (dist >= Short.MIN_VALUE + && dist <= Short.MAX_VALUE) + break; + } + } + throw new InternalError(); + } + } else { + dist = blockAddr[dest.getBlockNr()] - addr; + } + output.writeByte(opcode); + output.writeShort(dist); + } + break; + } + + case opc_lookupswitch: { + int align = 3-(addr % 4); + int[] values = instr.getValues(); + int npairs = values.length; + Block defBlock = succs[npairs]; + int defAddr = defBlock == null ? lastRetAddr + : blockAddr[defBlock.getBlockNr()]; + + if (npairs > 0) { + int tablesize = values[npairs-1] - values[0] + 1; + if (4 + tablesize * 4 < 8 * npairs) { + // Use a table switch + output.writeByte(opc_tableswitch); + output.write(new byte[align]); + /* def */ + output.writeInt(defAddr - addr); + /* low */ + output.writeInt(values[0]); + /* high */ + output.writeInt(values[npairs-1]); + int pos = values[0]; + for (int k = 0; k < npairs; k++) { + while (pos++ < values[k]) + output.writeInt(defAddr - addr); + int dest = succs[k] == null ? lastRetAddr + : blockAddr[succs[k].getBlockNr()]; + output.writeInt(dest - addr); + } + continue next_block; + } + } + // Use a lookup switch + output.writeByte(opc_lookupswitch); + output.write(new byte[align]); + /* def */ + output.writeInt(defAddr - addr); + output.writeInt(npairs); + for (int k = 0; k < npairs; k++) { + output.writeInt(values[k]); + int dest = succs[k] == null ? lastRetAddr + : blockAddr[succs[k].getBlockNr()]; + output.writeInt(dest - addr); + } + continue next_block; + } + + case opc_getstatic: + case opc_getfield: + case opc_putstatic: + case opc_putfield: + output.writeByte(opcode); + output.writeShort(gcp.putRef(gcp.FIELDREF, + instr.getReference())); + break; + + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_invokevirtual: { + Reference ref = instr.getReference(); + output.writeByte(opcode); + if (opcode == opc_invokeinterface) { + output.writeShort + (gcp.putRef(gcp.INTERFACEMETHODREF, ref)); + output.writeByte + (TypeSignature.getArgumentSize(ref.getType()) + 1); + output.writeByte(0); + } else + output.writeShort(gcp.putRef(gcp.METHODREF, ref)); + break; + } + case opc_new: + case opc_checkcast: + case opc_instanceof: + output.writeByte(opcode); + output.writeShort(gcp.putClassType(instr.getClazzType())); + break; + case opc_multianewarray: + if (instr.getDimensions() == 1) { + String clazz = instr.getClazzType().substring(1); + int index = newArrayTypes.indexOf(clazz.charAt(0)); + if (index != -1) { + output.writeByte(opc_newarray); + output.writeByte(index + 4); + } else { + output.writeByte(opc_anewarray); + output.writeShort(gcp.putClassType(clazz)); + } + } else { + output.writeByte(opcode); + output.writeShort + (gcp.putClassType(instr.getClazzType())); + output.writeByte(instr.getDimensions()); + } + break; + + case opc_ireturn: case opc_lreturn: + case opc_freturn: case opc_dreturn: case opc_areturn: + case opc_athrow: case opc_return: + continue next_block; + + case opc_nop: + case opc_iaload: case opc_laload: case opc_faload: + case opc_daload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: + case opc_iastore: case opc_lastore: case opc_fastore: + case opc_dastore: case opc_aastore: + case opc_bastore: case opc_castore: case opc_sastore: + case opc_pop: case opc_pop2: + case opc_dup: case opc_dup_x1: case opc_dup_x2: + case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: + case opc_swap: + case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: + case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: + case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: + case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: + case opc_irem: case opc_lrem: case opc_frem: case opc_drem: + case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: + case opc_ishl: case opc_lshl: + case opc_ishr: case opc_lshr: + case opc_iushr: case opc_lushr: + case opc_iand: case opc_land: + case opc_ior: case opc_lor: + case opc_ixor: case opc_lxor: + case opc_i2l: case opc_i2f: case opc_i2d: + case opc_l2i: case opc_l2f: case opc_l2d: + case opc_f2i: case opc_f2l: case opc_f2d: + case opc_d2i: case opc_d2l: case opc_d2f: + case opc_i2b: case opc_i2c: case opc_i2s: + case opc_lcmp: case opc_fcmpl: case opc_fcmpg: + case opc_dcmpl: case opc_dcmpg: + case opc_arraylength: + case opc_monitorenter: case opc_monitorexit: + output.writeByte(opcode); + break; + default: + throw new ClassFormatException("Invalid opcode "+opcode); + } + addr += instrLength[i][j]; + } + // Check which type of goto we should use at end of this block. + Block defaultSucc = succs[succs.length - 1]; + if (isRet.get(i+1)) { + output.writeByte(opc_return); + addr++; + } else if (isWide.get(i+1)) { + output.writeByte(opc_goto_w); + output.writeInt(blockAddr[defaultSucc.getBlockNr()] - addr); + addr+=5; + } else if (defaultSucc.getBlockNr() != i+1) { + output.writeByte(opc_goto); + output.writeShort(blockAddr[defaultSucc.getBlockNr()] - addr); + addr+=3; + } + } + if (retAtEnd) { + output.writeByte(opc_return); + addr++; + } + if (addr != blockAddr[blocks.length]) + throw new InternalError("Address calculation broken!"); + + Handler[] handlers = bb.getExceptionHandlers(); + output.writeShort(handlers.length); + for (int i = 0; i< handlers.length; i++) { + output.writeShort(blockAddr[handlers[i].start.getBlockNr()]); + output.writeShort(blockAddr[handlers[i].end.getBlockNr()+1]); + output.writeShort(blockAddr[handlers[i].catcher.getBlockNr()]); + output.writeShort((handlers[i].type == null) ? 0 + : gcp.putClassName(handlers[i].type)); + } + } +} + diff --git a/jode/jode/bytecode/BasicBlocks.java b/jode/jode/bytecode/BasicBlocks.java new file mode 100644 index 0000000..ad7464b --- /dev/null +++ b/jode/jode/bytecode/BasicBlocks.java @@ -0,0 +1,318 @@ +/* BasicBlocks Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +import jode.GlobalOptions; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.PrintWriter; + +///#def COLLECTIONS java.util +import java.util.ArrayList; +import java.util.Iterator; +import java.util.NoSuchElementException; +///#enddef +///#def COLLECTIONEXTRA java.lang +import java.lang.UnsupportedOperationException; +///#enddef + +/** + *

This class gives another representation of the byte code of a + * method. The instructions are splitted into BasicBlocks, each an + * array of consecutive Instructions. It is not allowed that a goto + * jumps inside a basic block.

+ * + *

All jumps must be at the end of the block. A conditional jump + * may be followed by a single goto, but there must not be any other + * jumps. If there is now unconditional jump at the end, the block + * implicitely flows into the next one.

+ * + *

Try block must span over some consecutive BasicBlocks and there + * catches must jump to the start of an basic block.

+ * + *

Deadcode will not be included in the BasicBlock, also + * BasicBlocks consisting of a single jump will be optimized away.

+ * + * @see jode.bytecode.Instruction */ +public class BasicBlocks extends BinaryInfo { + + /** + * The method info which contains the basic blocks. + */ + private MethodInfo methodInfo; + /** + * The maximal number of stack entries, that may be used in this + * method. + */ + int maxStack; + /** + * The maximal number of local slots, that may be used in this + * method. + */ + int maxLocals; + + /** + * This is an array of blocks, which are arrays + * of Instructions. + */ + private Block[] blocks; + + /** + * The start block. Normally the first block, but differs if method start + * with a goto, e.g a while. This may be null, if this method is empty. + */ + private Block startBlock; + + /** + * The local variable infos for the method parameters. + */ + private LocalVariableInfo[] paramInfos; + + /** + * The array of exception handlers. + */ + private Handler[] exceptionHandlers; + + public BasicBlocks(MethodInfo mi) { + methodInfo = mi; + int paramSize = (mi.isStatic() ? 0 : 1) + + TypeSignature.getArgumentSize(mi.getType()); + paramInfos = new LocalVariableInfo[paramSize]; + for (int i=0; i< paramSize; i++) + paramInfos[i] = LocalVariableInfo.getInfo(i); + } + + public int getMaxStack() { + return maxStack; + } + + public int getMaxLocals() { + return maxLocals; + } + + public MethodInfo getMethodInfo() { + return methodInfo; + } + + public Block getStartBlock() { + return startBlock; + } + + public Block[] getBlocks() { + return blocks; + } + + public Iterator getAllInstructions() { + return new Iterator() { + int blockNr = 0; + Iterator blockIter = getNextIterator(); + + public boolean hasNext() { + return blockIter != null; + } + + public Iterator getNextIterator() { + if (blockNr < blocks.length) + return blocks[blockNr++].getInstructions().iterator(); + return null; + } + + public Object next() { + Object instr; + try { + instr = blockIter.next(); + } catch (NullPointerException ex) { + throw new NoSuchElementException(); + } + if (!blockIter.hasNext()) + blockIter = getNextIterator(); + return instr; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * @return the exception handlers, or null if the method has no + * exception handlers. + */ + public Handler[] getExceptionHandlers() { + return exceptionHandlers; + } + + public LocalVariableInfo getParamInfo(int i) { + return paramInfos[i]; + } + + public int getParamCount() { + return paramInfos.length; + } + + public void setMaxStack(int ms) { + maxStack = ms; + } + + public void setMaxLocals(int ml) { + maxLocals = ml; + } + + public void setBlocks(Block[] blocks, Block startBlock) { + for (int i = 0; i < blocks.length; i++) + blocks[i].blockNr = i; + this.blocks = blocks; + this.startBlock = startBlock; + this.exceptionHandlers = null; + } + + public void setExceptionHandlers(Handler[] handlers) { + exceptionHandlers = handlers.length == 0 ? Handler.EMPTY : handlers; + ArrayList activeHandlers = new ArrayList(); + for (int i = 0; i < blocks.length; i++) { + for (int j = 0; j < handlers.length; j++) { + if (handlers[j].getStart() == blocks[i]) + activeHandlers.add(handlers[j]); + if (handlers[j].getEnd() == blocks[i]) + activeHandlers.remove(handlers[j]); + } + if (activeHandlers.size() == 0) + blocks[i].catchers = Handler.EMPTY; + else + blocks[i].catchers = + (Handler[]) activeHandlers.toArray(Handler.EMPTY); + } + } + + /** + * Sets the name and type of a method parameter. This overwrites + * any previously set parameter info for this slot. + * @param info a local variable info mapping a slot nr to a name + * and a type. + */ + public void setParamInfo(LocalVariableInfo info) { + paramInfos[info.getSlot()] = info; + } + + private BasicBlockReader reader; + public void read(ConstantPool cp, + DataInputStream input, + int howMuch) throws IOException { + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + GlobalOptions.err.println("Reading "+methodInfo); + reader = new BasicBlockReader(this); + reader.readCode(cp, input); + readAttributes(cp, input, howMuch); + reader.convert(); + reader = null; + if ((GlobalOptions.debuggingFlags + & GlobalOptions.DEBUG_BYTECODE) != 0) + dumpCode(GlobalOptions.err); + } + + protected void readAttribute(String name, int length, ConstantPool cp, + DataInputStream input, + int howMuch) throws IOException { + if (howMuch >= ClassInfo.ALMOSTALL + && name.equals("LocalVariableTable")) { + reader.readLVT(length, cp, input); + } else if (howMuch >= ClassInfo.ALMOSTALL + && name.equals("LineNumberTable")) { + reader.readLNT(length, cp, input); + } else + super.readAttribute(name, length, cp, input, howMuch); + } + + + void reserveSmallConstants(GrowableConstantPool gcp) { + for (int i=0; i < blocks.length; i++) { + next_instr: + for (Iterator iter = blocks[i].getInstructions().iterator(); + iter.hasNext(); ) { + Instruction instr = (Instruction) iter.next(); + if (instr.getOpcode() == Opcodes.opc_ldc) { + Object constant = instr.getConstant(); + if (constant == null) + continue next_instr; + for (int j=1; j < Opcodes.constants.length; j++) { + if (constant.equals(Opcodes.constants[j])) + continue next_instr; + } + if (constant instanceof Integer) { + int value = ((Integer) constant).intValue(); + if (value >= Short.MIN_VALUE + && value <= Short.MAX_VALUE) + continue next_instr; + } + gcp.reserveConstant(constant); + } + } + } + } + + BasicBlockWriter bbw; + void prepareWriting(GrowableConstantPool gcp) { + bbw = new BasicBlockWriter(this, gcp); + } + + int getKnownAttributeCount() { + return bbw.getAttributeCount(); + } + + void writeKnownAttributes(GrowableConstantPool gcp, + DataOutputStream output) + throws IOException { + bbw.writeAttributes(gcp, output); + } + + void write(GrowableConstantPool gcp, + DataOutputStream output) throws IOException { + output.writeInt(bbw.getSize() + getAttributeSize()); + bbw.write(gcp, output); + writeAttributes(gcp, output); + bbw = null; + } + + public void dumpCode(PrintWriter output) { + output.println(methodInfo.getName()+methodInfo.getType()+":"); + if (startBlock == null) + output.println("\treturn"); + else if (startBlock != blocks[0]) + output.println("\tgoto "+startBlock); + + for (int i=0; i< blocks.length; i++) { + blocks[i].dumpCode(output); + } + for (int i=0; i< exceptionHandlers.length; i++) { + output.println("catch " + exceptionHandlers[i].type + + " from " + exceptionHandlers[i].start + + " to " + exceptionHandlers[i].end + + " catcher " + exceptionHandlers[i].catcher); + } + } + + public String toString() { + return "BasicBlocks["+methodInfo+"]"; + } +} diff --git a/jode/jode/bytecode/BinaryInfo.java.in b/jode/jode/bytecode/BinaryInfo.java similarity index 77% rename from jode/jode/bytecode/BinaryInfo.java.in rename to jode/jode/bytecode/BinaryInfo.java index d992619..a2d3e44 100644 --- a/jode/jode/bytecode/BinaryInfo.java.in +++ b/jode/jode/bytecode/BinaryInfo.java @@ -26,31 +26,21 @@ import java.io.IOException; import java.io.InputStream; import jode.util.SimpleMap; -import @COLLECTIONS@.Map; -import @COLLECTIONS@.Collections; -import @COLLECTIONS@.Iterator; +///#def COLLECTIONS java.util +import java.util.Map; +import java.util.Collections; +import java.util.Iterator; +///#enddef /** * * @author Jochen Hoenicke */ -public class BinaryInfo { - public static final int HIERARCHY = 0x01; - public static final int FIELDS = 0x02; - public static final int METHODS = 0x04; - public static final int CONSTANTS = 0x08; - public static final int KNOWNATTRIBS = 0x10; - public static final int INNERCLASSES = 0x20; - public static final int OUTERCLASSES = 0x40; - public static final int UNKNOWNATTRIBS = 0x80; - public static final int FULLINFO = 0xff; - public static final int MOSTINFO = 0x7f; - public static final int REFLECTINFO = 0x6f; - +class BinaryInfo { private Map unknownAttributes = null; - protected void skipAttributes(DataInputStream input) throws IOException { + void skipAttributes(DataInputStream input) throws IOException { int count = input.readUnsignedShort(); for (int i=0; i< count; i++) { input.readUnsignedShort(); // the name index @@ -64,17 +54,17 @@ public class BinaryInfo { } } - protected int getKnownAttributeCount() { + int getKnownAttributeCount() { return 0; } - protected void readAttribute(String name, int length, - ConstantPool constantPool, - DataInputStream input, - int howMuch) throws IOException { + void readAttribute(String name, int length, + ConstantPool constantPool, + DataInputStream input, + int howMuch) throws IOException { byte[] data = new byte[length]; input.readFully(data); - if ((howMuch & UNKNOWNATTRIBS) != 0) { + if (howMuch >= ClassInfo.ALL) { if (unknownAttributes == null) unknownAttributes = new SimpleMap(); unknownAttributes.put(name, data); @@ -132,9 +122,9 @@ public class BinaryInfo { } } - protected void readAttributes(ConstantPool constantPool, - DataInputStream input, - int howMuch) throws IOException { + void readAttributes(ConstantPool constantPool, + DataInputStream input, + int howMuch) throws IOException { int count = input.readUnsignedShort(); unknownAttributes = null; for (int i=0; i< count; i++) { @@ -150,12 +140,11 @@ public class BinaryInfo { } } - public void dropInfo(int howMuch) { - if ((howMuch & UNKNOWNATTRIBS) != 0) - unknownAttributes = null; + public void dropAttributes() { + unknownAttributes = null; } - protected void prepareAttributes(GrowableConstantPool gcp) { + void prepareAttributes(GrowableConstantPool gcp) { if (unknownAttributes == null) return; Iterator i = unknownAttributes.keySet().iterator(); @@ -163,12 +152,12 @@ public class BinaryInfo { gcp.putUTF8((String) i.next()); } - protected void writeKnownAttributes + void writeKnownAttributes (GrowableConstantPool constantPool, DataOutputStream output) throws IOException { } - protected void writeAttributes + void writeAttributes (GrowableConstantPool constantPool, DataOutputStream output) throws IOException { int count = getKnownAttributeCount(); diff --git a/jode/jode/bytecode/Block.java b/jode/jode/bytecode/Block.java new file mode 100644 index 0000000..2ebb456 --- /dev/null +++ b/jode/jode/bytecode/Block.java @@ -0,0 +1,212 @@ +/* Block Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +import java.io.PrintWriter; +///#def COLLECTIONS java.util +import java.util.Collection; +import java.util.Arrays; +import java.util.List; +import java.util.Iterator; +///#enddef + +/** + * Represents a single basic block. It contains a list of + * instructions, the successor blocks and the exception handlers for + * this block. The last Instruction, and only the last, may be a + * conditional jump, a tableswitch or a jsr. + * + * @author Jochen Hoenicke */ +public final class Block { + /** + * The opcodes of the instructions in this block. + */ + private Instruction[] instrs; + + /** + * The blockNr of successor blocks + */ + private Block[] succs; + + /** + * The catching blocks. Set by BasicBlocks. + */ + Handler[] catchers; + + /** + * The blockNr of this block. Set by BasicBlocks. + */ + int blockNr; + + /** + * Creates a new empty block, with a null successor array. + */ + public Block() { + instrs = new Instruction[0]; + succs = null; + } + + /** + * Gets the list of instructions. The returned list should not be + * modified, except that the instructions (but not their opcodes) + * may be modified. + */ + public List getInstructions() { + return Arrays.asList(instrs); + } + + /** + * Gets the successor array. The last successor is the next basic + * block that is jumped to via goto or the default part of a + * switch. For conditional jumps and jsrs the second successor gives + * the destination. + */ + public Block[] getSuccs() { + return succs; + } + + /** + * Gets the exception handlers which try part contains this block. + * You can't set them since they are calculated automatically. + * @return the exception handlers. + * @see BasicBlocks#setExceptionHandlers + */ + public Handler[] getCatchers() { + return catchers; + } + + /** + * Gets the block number. The block numbers are consecutive number + * from 0 to the number of blocks in a method. The equation + *
 BasicBlocks.getBlock()[i].getBlockNr() == i 
+ * always holds (as long as you don't do something dirty, like adding + * the same block to different BasicBlocks, or to the same but more + * than once). + * @return the block number. + */ + public int getBlockNr() { + return blockNr; + } + + private void checkConsistent() { + /* Check if all instructions are of correct type */ + int size = instrs.length; + for (int i = 0; i < size; i++) { + int opcode = instrs[i].getOpcode(); + switch (opcode) { + case Opcodes.opc_goto: + throw new IllegalArgumentException("goto in block"); + + case Opcodes.opc_lookupswitch: + if (succs == null || succs.length == 0) + throw new IllegalArgumentException + ("no successors for switch"); + if (i != size - 1) + throw new IllegalArgumentException + ("switch in the middle!"); + return; + + case Opcodes.opc_ret: case Opcodes.opc_athrow: + case Opcodes.opc_ireturn: case Opcodes.opc_lreturn: + case Opcodes.opc_freturn: case Opcodes.opc_dreturn: + case Opcodes.opc_areturn: case Opcodes.opc_return: + if (succs == null || succs.length > 0) + throw new IllegalArgumentException + ("throw or return with successor."); + if (i != size - 1) + throw new IllegalArgumentException + ("return in the middle!"); + return; + + case Opcodes.opc_ifeq: case Opcodes.opc_ifne: + case Opcodes.opc_iflt: case Opcodes.opc_ifge: + case Opcodes.opc_ifgt: case Opcodes.opc_ifle: + case Opcodes.opc_if_icmpeq: case Opcodes.opc_if_icmpne: + case Opcodes.opc_if_icmplt: case Opcodes.opc_if_icmpge: + case Opcodes.opc_if_icmpgt: case Opcodes.opc_if_icmple: + case Opcodes.opc_if_acmpeq: case Opcodes.opc_if_acmpne: + case Opcodes.opc_ifnull: case Opcodes.opc_ifnonnull: + case Opcodes.opc_jsr: + if (succs == null || succs.length != 2) + throw new IllegalArgumentException + ("successors inappropriate for if/jsr"); + if (i != size - 1) + throw new IllegalArgumentException + ("if/jsr in the middle!"); + return; + } + } + if (succs == null || succs.length != 1) + throw new IllegalArgumentException("no single successor block"); + } + + /** + * Set the code, i.e. instructions and successor blocks. + * The instructions must be valid and match the successors. + */ + public void setCode(Collection instrs, Block[] succs) { + this.instrs = (Instruction[]) + instrs.toArray(new Instruction[instrs.size()]); + this.succs = succs; + try { + checkConsistent(); + } catch (IllegalArgumentException ex) { + dumpCode(jode.GlobalOptions.err); + throw ex; + } + } + + public void dumpCode(PrintWriter output) { + output.println(" "+this+":"); + for (int i = 0; i < instrs.length; i++) { + Instruction instr = instrs[i]; + if (i == instrs.length - 1 && succs != null) { + int opcode = instr.getOpcode(); + if (opcode == Opcodes.opc_lookupswitch) { + // Special case for switch: + output.println("\tswitch"); + int[] values = instr.getValues(); + for (int j = 0; j < values.length; j++) + output.println("\t case"+values[j] + +": goto "+succs[j]); + output.println("\t default: goto"+ + succs[values.length]); + return; + } else if (succs.length > 1) { + output.println("\t"+instr.getDescription() + +" "+succs[0]); + break; + } + } + output.println("\t"+instr.getDescription()); + + } + if (succs != null && succs.length > 0) { + if (succs[succs.length-1] == null) + output.println("\treturn"); + else + output.println("\tgoto "+succs[succs.length-1]); + } + } + + public String toString() { + return "Block_"+blockNr; + } +} diff --git a/jode/jode/bytecode/BytecodeInfo.java.in b/jode/jode/bytecode/BytecodeInfo.java.in deleted file mode 100644 index d7a0169..0000000 --- a/jode/jode/bytecode/BytecodeInfo.java.in +++ /dev/null @@ -1,1509 +0,0 @@ -/* BytecodeInfo Copyright (C) 1999 Jochen Hoenicke. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -package jode.bytecode; -import jode.GlobalOptions; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.EOFException; -import java.io.IOException; -import java.util.Vector; -import java.util.Enumeration; -import java.util.NoSuchElementException; - -import @COLLECTIONS@.List; -import @COLLECTIONS@.AbstractSequentialList; -import @COLLECTIONS@.Iterator; -import @COLLECTIONS@.ListIterator; - - -/** - * This class represents the byte code of a method. Each instruction is - * stored in an Instruction instance. - * - * We canonicalize some opcodes: wide opcodes are mapped to short ones, - * opcodes that load a constant are mapped to opc_ldc or opc_ldc2_w, and - * opc_xload_x / opc_xstore_x opcodes are mapped to opc_xload / opc_xstore. - */ -public class BytecodeInfo extends BinaryInfo implements Opcodes { - - private MethodInfo methodInfo; - private int maxStack, maxLocals; - private Handler[] exceptionHandlers; - private LocalVariableInfo[] lvt; - private LineNumber[] lnt; - - /** - * A array of instructions, indexed by address. This array is only - * valid while reading the code. - */ - private Instruction[] instrs; - - private InstructionList instructions; - - private class InstructionList extends AbstractSequentialList { - Instruction borderInstr; - int instructionCount = 0; - - InstructionList() { - // opc_impdep1 is used as border instruction, it may not - // occur in bytecode. - borderInstr = new Instruction(opc_impdep1); - borderInstr.nextByAddr = borderInstr.prevByAddr = borderInstr; - } - - public int size() { - return instructionCount; - } - - Instruction get0(int index) { - Instruction instr = borderInstr; - if (index < instructionCount / 2) { - for (int i=0; i <= index; i++) - instr = instr.nextByAddr; - } else { - for (int i=instructionCount; i > index; i--) - instr = instr.prevByAddr; - } - return instr; - } - - public Object get(int index) { - if (index < 0 || index >= instructionCount) - throw new IllegalArgumentException(); - return get0(index); - } - - public boolean add(Object o) { - /* optimize add, since it is called many times by read() */ - instructionCount++; - borderInstr.prevByAddr.appendInstruction((Instruction) o, - BytecodeInfo.this); - return true; - } - - public ListIterator listIterator(final int startIndex) { - if (startIndex < 0 || startIndex > instructionCount) - throw new IllegalArgumentException(); - return new ListIterator() { - Instruction instr = get0(startIndex); - Instruction toRemove = null; - int index = startIndex; - - public boolean hasNext() { - return index < instructionCount; - } - - public boolean hasPrevious() { - return index > 0; - } - - public Object next() { - if (index >= instructionCount) - throw new NoSuchElementException(); - index++; - toRemove = instr; - instr = instr.nextByAddr; -// System.err.println("next: "+toRemove.getDescription()); - return toRemove; - } - - public Object previous() { - if (index == 0) - throw new NoSuchElementException(); - index--; - instr = instr.prevByAddr; - toRemove = instr; -// System.err.println("prev: "+toRemove.getDescription()); - return toRemove; - } - - public int nextIndex() { - return index; - } - - public int previousIndex() { - return index - 1; - } - - public void remove() { - if (toRemove == null) - throw new IllegalStateException(); -// System.err.println("remove: "+toRemove.getDescription()); - instructionCount--; - if (instr == toRemove) - instr = instr.nextByAddr; - else - index--; - toRemove.removeInstruction(BytecodeInfo.this); - toRemove = null; - } - - public void add(Object o) { - instructionCount++; - index++; -// System.err.println("add: " -// +((Instruction)o).getDescription() -// +" after "+instr.prevByAddr -// .getDescription()); - instr.prevByAddr.appendInstruction((Instruction) o, - BytecodeInfo.this); - toRemove = null; - } - - public void set(Object o) { - if (toRemove == null) - throw new IllegalStateException(); -// System.err.println("replace "+toRemove.getDescription() -// +" with " -// +((Instruction)o).getDescription()); - toRemove.replaceInstruction((Instruction) o, - BytecodeInfo.this); - if (instr == toRemove) - instr = (Instruction) o; - toRemove = (Instruction) o; - } - }; - } - - void setLastAddr(int addr) { - borderInstr.setAddr(addr); - } - - int getCodeLength() { - return borderInstr.getAddr(); - } - } - - public BytecodeInfo(MethodInfo mi) { - methodInfo = mi; - } - - private final static Object[] constants = { - null, - new Integer(-1), new Integer(0), new Integer(1), - new Integer(2), new Integer(3), new Integer(4), new Integer(5), - new Long(0), new Long(1), - new Float(0), new Float(1), new Float(2), - new Double(0), new Double(1) - }; - - protected void readAttribute(String name, int length, ConstantPool cp, - DataInputStream input, - int howMuch) throws IOException { - if ((howMuch & KNOWNATTRIBS) != 0 - && name.equals("LocalVariableTable")) { - if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0) - GlobalOptions.err.println("LocalVariableTable of "+methodInfo); - int count = input.readUnsignedShort(); - if (length != 2 + count * 10) { - if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0) - GlobalOptions.err.println("Illegal LVT length, ignoring it"); - return; - } - lvt = new LocalVariableInfo[count]; - for (int i=0; i < count; i++) { - lvt[i] = new LocalVariableInfo(); - int start = input.readUnsignedShort(); - int end = start + input.readUnsignedShort(); - int nameIndex = input.readUnsignedShort(); - int typeIndex = input.readUnsignedShort(); - int slot = input.readUnsignedShort(); - Instruction startInstr = - start >= 0 && start < instrs.length ? instrs[start] : null; - Instruction endInstr; - if (end >=0 && end < instrs.length) - endInstr = instrs[end] == null ? null - : instrs[end].getPrevByAddr(); - else { - endInstr = null; - for (int nr = instrs.length - 1; nr >= 0; nr--) { - if (instrs[nr] != null) { - if (instrs[nr].getNextAddr() == end) - endInstr = instrs[nr]; - break; - } - } - } - - if (startInstr == null - || endInstr == null - || nameIndex == 0 || typeIndex == 0 - || slot >= maxLocals - || cp.getTag(nameIndex) != cp.UTF8 - || cp.getTag(typeIndex) != cp.UTF8) { - - // This is probably an evil lvt as created by HashJava - // simply ignore it. - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_LVT) != 0) - GlobalOptions.err.println - ("Illegal entry, ignoring LVT"); - lvt = null; - return; - } - lvt[i].start = startInstr; - lvt[i].end = endInstr; - lvt[i].name = cp.getUTF8(nameIndex); - lvt[i].type = cp.getUTF8(typeIndex); - lvt[i].slot = slot; - if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0) - GlobalOptions.err.println("\t" + lvt[i].name + ": " - + lvt[i].type - +" range "+start+" - "+end - +" slot "+slot); - } - } else if ((howMuch & KNOWNATTRIBS) != 0 - && name.equals("LineNumberTable")) { - int count = input.readUnsignedShort(); - if (length != 2 + count * 4) { - GlobalOptions.err.println - ("Illegal LineNumberTable, ignoring it"); - return; - } - lnt = new LineNumber[count]; - for (int i = 0; i < count; i++) { - lnt[i] = new LineNumber(); - int start = input.readUnsignedShort(); - Instruction startInstr = instrs[start]; - if (startInstr == null) { - GlobalOptions.err.println - ("Illegal entry, ignoring LineNumberTable table"); - lnt = null; - return; - } - lnt[i].start = startInstr; - lnt[i].linenr = input.readUnsignedShort(); - } - } else - super.readAttribute(name, length, cp, input, howMuch); - } - - public void read(ConstantPool cp, - DataInputStream input) throws IOException { - maxStack = input.readUnsignedShort(); - maxLocals = input.readUnsignedShort(); - instructions = new InstructionList(); - int codeLength = input.readInt(); - instrs = new Instruction[codeLength]; - int[][] succAddrs = new int[codeLength][]; - { - int addr = 0; - while (addr < codeLength) { - Instruction instr; - int length; - int opcode = input.readUnsignedByte(); - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(addr+": "+opcodeString[opcode]); - - switch (opcode) { - case opc_wide: { - int wideopcode = input.readUnsignedByte(); - switch (wideopcode) { - case opc_iload: case opc_fload: case opc_aload: - case opc_istore: case opc_fstore: case opc_astore: { - int slot = input.readUnsignedShort(); - if (slot >= maxLocals) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(wideopcode); - instr.setLocalSlot(slot); - length = 4; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print - (" " + opcodeString[wideopcode] + " " + slot); - break; - } - case opc_lload: case opc_dload: - case opc_lstore: case opc_dstore: { - int slot = input.readUnsignedShort(); - if (slot >= maxLocals-1) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(wideopcode); - instr.setLocalSlot(slot); - length = 4; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print - (" " + opcodeString[wideopcode] + " " + slot); - break; - } - case opc_ret: { - int slot = input.readUnsignedShort(); - if (slot >= maxLocals) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(wideopcode); - instr.setLocalSlot(slot); - length = 4; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" ret "+slot); - break; - } - case opc_iinc: { - int slot = input.readUnsignedShort(); - if (slot >= maxLocals) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(wideopcode); - instr.setLocalSlot(slot); - instr.setIncrement(input.readShort()); - length = 6; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print - (" iinc " + slot + " " + instr.getIncrement()); - break; - } - default: - throw new ClassFormatError("Invalid wide opcode " - +wideopcode); - } - break; - } - case opc_iload_0: case opc_iload_1: - case opc_iload_2: case opc_iload_3: - case opc_lload_0: case opc_lload_1: - case opc_lload_2: case opc_lload_3: - case opc_fload_0: case opc_fload_1: - case opc_fload_2: case opc_fload_3: - case opc_dload_0: case opc_dload_1: - case opc_dload_2: case opc_dload_3: - case opc_aload_0: case opc_aload_1: - case opc_aload_2: case opc_aload_3: { - int slot = (opcode-opc_iload_0) & 3; - if (slot >= maxLocals) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(opc_iload + - (opcode-opc_iload_0)/4); - instr.setLocalSlot(slot); - length = 1; - break; - } - case opc_istore_0: case opc_istore_1: - case opc_istore_2: case opc_istore_3: - case opc_fstore_0: case opc_fstore_1: - case opc_fstore_2: case opc_fstore_3: - case opc_astore_0: case opc_astore_1: - case opc_astore_2: case opc_astore_3: { - int slot = (opcode-opc_istore_0) & 3; - if (slot >= maxLocals) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(opc_istore + - (opcode-opc_istore_0)/4); - instr.setLocalSlot(slot); - length = 1; - break; - } - case opc_lstore_0: case opc_lstore_1: - case opc_lstore_2: case opc_lstore_3: - case opc_dstore_0: case opc_dstore_1: - case opc_dstore_2: case opc_dstore_3: { - int slot = (opcode-opc_istore_0) & 3; - if (slot >= maxLocals-1) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(opc_istore - + (opcode-opc_istore_0)/4); - instr.setLocalSlot(slot); - length = 1; - break; - } - case opc_iload: case opc_fload: case opc_aload: - case opc_istore: case opc_fstore: case opc_astore: { - int slot = input.readUnsignedByte(); - if (slot >= maxLocals) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(opcode); - instr.setLocalSlot(slot); - length = 2; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+slot); - break; - } - case opc_lstore: case opc_dstore: - case opc_lload: case opc_dload: { - int slot = input.readUnsignedByte(); - if (slot >= maxLocals - 1) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(opcode); - instr.setLocalSlot(slot); - length = 2; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+slot); - break; - } - case opc_ret: { - int slot = input.readUnsignedByte(); - if (slot >= maxLocals) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(opcode); - instr.setLocalSlot(slot); - length = 2; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+slot); - break; - } - case opc_aconst_null: - case opc_iconst_m1: - case opc_iconst_0: case opc_iconst_1: case opc_iconst_2: - case opc_iconst_3: case opc_iconst_4: case opc_iconst_5: - case opc_fconst_0: case opc_fconst_1: case opc_fconst_2: - instr = new Instruction(opc_ldc); - instr.setConstant - (constants[opcode - opc_aconst_null]); - length = 1; - break; - case opc_lconst_0: case opc_lconst_1: - case opc_dconst_0: case opc_dconst_1: - instr = new Instruction(opc_ldc2_w); - instr.setConstant - (constants[opcode - opc_aconst_null]); - length = 1; - break; - case opc_bipush: - instr = new Instruction(opc_ldc); - instr.setConstant(new Integer(input.readByte())); - length = 2; - break; - case opc_sipush: - instr = new Instruction(opc_ldc); - instr.setConstant(new Integer(input.readShort())); - length = 3; - break; - case opc_ldc: { - int index = input.readUnsignedByte(); - int tag = cp.getTag(index); - if (tag != cp.STRING - && tag != cp.INTEGER && tag != cp.FLOAT) - throw new ClassFormatException - ("wrong constant tag: "+tag); - instr = new Instruction(opcode); - instr.setConstant(cp.getConstant(index)); - length = 2; - break; - } - case opc_ldc_w: { - int index = input.readUnsignedShort(); - int tag = cp.getTag(index); - if (tag != cp.STRING - && tag != cp.INTEGER && tag != cp.FLOAT) - throw new ClassFormatException - ("wrong constant tag: "+tag); - instr = new Instruction(opc_ldc); - instr.setConstant(cp.getConstant(index)); - length = 3; - break; - } - case opc_ldc2_w: { - int index = input.readUnsignedShort(); - int tag = cp.getTag(index); - if (tag != cp.LONG && tag != cp.DOUBLE) - throw new ClassFormatException - ("wrong constant tag: "+tag); - instr = new Instruction(opcode); - instr.setConstant(cp.getConstant(index)); - length = 3; - break; - } - case opc_iinc: { - int slot = input.readUnsignedByte(); - if (slot >= maxLocals) - throw new ClassFormatError - ("Invalid local slot "+slot); - instr = new Instruction(opcode); - instr.setLocalSlot(slot); - instr.setIncrement(input.readByte()); - length = 3; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print - (" " + slot + " " + instr.getIncrement()); - break; - } - case opc_goto: - case opc_jsr: - case opc_ifeq: case opc_ifne: - case opc_iflt: case opc_ifge: - case opc_ifgt: case opc_ifle: - case opc_if_icmpeq: case opc_if_icmpne: - case opc_if_icmplt: case opc_if_icmpge: - case opc_if_icmpgt: case opc_if_icmple: - case opc_if_acmpeq: case opc_if_acmpne: - case opc_ifnull: case opc_ifnonnull: - instr = new Instruction(opcode); - length = 3; - succAddrs[addr] = new int[] { addr+input.readShort() }; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+succAddrs[addr][0]); - break; - - case opc_goto_w: - case opc_jsr_w: - instr = new Instruction(opcode - (opc_goto_w - opc_goto)); - length = 5; - succAddrs[addr] = new int[] { addr+input.readInt() }; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+succAddrs[addr][0]); - break; - - case opc_tableswitch: { - length = 3 - (addr % 4); - input.readFully(new byte[length]); - int def = input.readInt(); - int low = input.readInt(); - int high = input.readInt(); - int[] dests = new int[high-low+1]; - int npairs = 0; - for (int i=0; i < dests.length; i++) { - dests[i] = input.readInt(); - if (dests[i] != def) - npairs++; - } - instr = new Instruction(opc_lookupswitch); - succAddrs[addr] = new int[npairs + 1]; - int[] values = new int[npairs]; - int pos = 0; - for (int i=0; i < dests.length; i++) { - if (dests[i] != def) { - values[pos] = i+low; - succAddrs[addr][pos] = addr + dests[i]; - pos++; - } - } - succAddrs[addr][npairs] = addr + def; - instr.setValues(values); - length += 13 + 4 * (high-low+1); - break; - } - case opc_lookupswitch: { - length = 3 - (addr % 4); - input.readFully(new byte[length]); - int def = input.readInt(); - int npairs = input.readInt(); - instr = new Instruction(opcode); - succAddrs[addr] = new int[npairs + 1]; - int[] values = new int[npairs]; - for (int i=0; i < npairs; i++) { - values[i] = input.readInt(); - if (i > 0 && values[i-1] >= values[i]) - throw new ClassFormatException - ("lookupswitch not sorted"); - succAddrs[addr][i] = addr + input.readInt(); - } - succAddrs[addr][npairs] = addr + def; - instr.setValues(values); - length += 9 + 8 * npairs; - break; - } - - case opc_getstatic: - case opc_getfield: - case opc_putstatic: - case opc_putfield: - case opc_invokespecial: - case opc_invokestatic: - case opc_invokevirtual: { - int index = input.readUnsignedShort(); - int tag = cp.getTag(index); - if (opcode < opc_invokevirtual) { - if (tag != cp.FIELDREF) - throw new ClassFormatException - ("field tag mismatch: "+tag); - } else { - if (tag != cp.METHODREF) - throw new ClassFormatException - ("method tag mismatch: "+tag); - } - Reference ref = cp.getRef(index); - if (ref.getName().charAt(0) == '<' - && (!ref.getName().equals("") - || opcode != opc_invokespecial)) - throw new ClassFormatException - ("Illegal call of special method/field "+ref); - instr = new Instruction(opcode); - instr.setReference(ref); - length = 3; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+ref); - break; - } - case opc_invokeinterface: { - int index = input.readUnsignedShort(); - int tag = cp.getTag(index); - if (tag != cp.INTERFACEMETHODREF) - throw new ClassFormatException - ("interface tag mismatch: "+tag); - Reference ref = cp.getRef(index); - if (ref.getName().charAt(0) == '<') - throw new ClassFormatException - ("Illegal call of special method "+ref); - int nargs = input.readUnsignedByte(); - if (TypeSignature.getArgumentSize(ref.getType()) - != nargs - 1) - throw new ClassFormatException - ("Interface nargs mismatch: "+ref+" vs. "+nargs); - if (input.readUnsignedByte() != 0) - throw new ClassFormatException - ("Interface reserved param not zero"); - - instr = new Instruction(opcode); - instr.setReference(ref); - length = 5; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+ref); - break; - } - - case opc_new: - case opc_checkcast: - case opc_instanceof: { - String type = cp.getClassType(input.readUnsignedShort()); - if (opcode == opc_new && type.charAt(0) == '[') - throw new ClassFormatException - ("Can't create array with opc_new"); - instr = new Instruction(opcode); - instr.setClazzType(type); - length = 3; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+type); - break; - } - case opc_multianewarray: { - String type = cp.getClassType(input.readUnsignedShort()); - int dims = input.readUnsignedByte(); - if (dims == 0) - throw new ClassFormatException - ("multianewarray dimension is 0."); - for (int i=0; i < dims; i++) { - /* Note that since type is a valid type - * signature, there must be a non bracket - * character, before the string is over. - * So there is no StringIndexOutOfBoundsException. - */ - if (type.charAt(i) != '[') - throw new ClassFormatException - ("multianewarray called for non array:"+ type); - } - instr = new Instruction(opcode); - instr.setClazzType(type); - instr.setDimensions(dims); - length = 4; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" " + type + " " + dims); - break; - } - case opc_anewarray: { - String type = cp.getClassType(input.readUnsignedShort()); - instr = new Instruction(opc_multianewarray); - instr.setClazzType(("["+type).intern()); - instr.setDimensions(1); - length = 3; - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+type); - break; - } - case opc_newarray: { - char sig = newArrayTypes.charAt - (input.readUnsignedByte()-4); - String type = new String (new char[] { '[', sig }); - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+type); - instr = new Instruction(opc_multianewarray); - instr.setClazzType(type); - instr.setDimensions(1); - length = 2; - break; - } - - case opc_nop: - case opc_iaload: case opc_laload: case opc_faload: - case opc_daload: case opc_aaload: - case opc_baload: case opc_caload: case opc_saload: - case opc_iastore: case opc_lastore: case opc_fastore: - case opc_dastore: case opc_aastore: - case opc_bastore: case opc_castore: case opc_sastore: - case opc_pop: case opc_pop2: - case opc_dup: case opc_dup_x1: case opc_dup_x2: - case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: - case opc_swap: - case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: - case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: - case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: - case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: - case opc_irem: case opc_lrem: case opc_frem: case opc_drem: - case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: - case opc_ishl: case opc_lshl: - case opc_ishr: case opc_lshr: - case opc_iushr: case opc_lushr: - case opc_iand: case opc_land: - case opc_ior: case opc_lor: - case opc_ixor: case opc_lxor: - case opc_i2l: case opc_i2f: case opc_i2d: - case opc_l2i: case opc_l2f: case opc_l2d: - case opc_f2i: case opc_f2l: case opc_f2d: - case opc_d2i: case opc_d2l: case opc_d2f: - case opc_i2b: case opc_i2c: case opc_i2s: - case opc_lcmp: case opc_fcmpl: case opc_fcmpg: - case opc_dcmpl: case opc_dcmpg: - case opc_ireturn: case opc_lreturn: - case opc_freturn: case opc_dreturn: case opc_areturn: - case opc_return: - case opc_athrow: - case opc_arraylength: - case opc_monitorenter: case opc_monitorexit: - instr = new Instruction(opcode); - length = 1; - break; - default: - throw new ClassFormatError("Invalid opcode "+opcode); - } - if ((GlobalOptions.debuggingFlags - & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.println(); - - instrs[addr] = instr; - instructions.add(instr); - - addr += length; - instructions.setLastAddr(addr); - } - if (addr != codeLength) - throw new ClassFormatError("last instruction too long"); - } - for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - int addr = instr.getAddr(); - if (succAddrs[addr] != null) { - int length = succAddrs[addr].length; - Instruction[] succs = new Instruction[length]; - for (int i=0; i < length; i++) { - int succAddr = succAddrs[addr][i]; - if (succAddr < 0 || succAddr > codeLength - || instrs[succAddr] == null) - throw new ClassFormatException - ("Illegal jump target at " - +this+"@"+addr); - succs[i] = instrs[succAddr]; - } - instr.setSuccs(succs); - } - } - succAddrs = null; - - { - int handlersLength = input.readUnsignedShort(); - exceptionHandlers = new Handler[handlersLength]; - for (int i=0; i< handlersLength; i ++) { - exceptionHandlers[i] = new Handler(); - exceptionHandlers[i].start - = instrs[input.readUnsignedShort()]; - exceptionHandlers[i].end - = instrs[input.readUnsignedShort()].getPrevByAddr(); - exceptionHandlers[i].catcher - = instrs[input.readUnsignedShort()]; - int index = input.readUnsignedShort(); - exceptionHandlers[i].type = (index == 0) ? null - : cp.getClassName(index); - } - } - readAttributes(cp, input, FULLINFO); - instrs = null; - } - - public void dumpCode(java.io.PrintWriter output) { - for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - output.println(instr.getDescription() + " " - + Integer.toHexString(hashCode())); - Instruction[] succs = instr.getSuccs(); - if (succs != null) { - output.print("\tsuccs: "+succs[0]); - for (int i = 1; i < succs.length; i++) - output.print(", "+succs[i]); - output.println(); - } - if (instr.getPreds() != null) { - output.print("\tpreds: " + instr.getPreds()[0]); - for (int i=1; i < instr.getPreds().length; i++) - output.print(", " + instr.getPreds()[i]); - output.println(); - } - } - for (int i=0; i< exceptionHandlers.length; i++) { - output.println("catch " + exceptionHandlers[i].type - + " from " + exceptionHandlers[i].start - + " to " + exceptionHandlers[i].end - + " catcher " + exceptionHandlers[i].catcher); - } - } - - public void reserveSmallConstants(GrowableConstantPool gcp) { - next_instr: - for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - if (instr.getOpcode() == opc_ldc) { - Object constant = instr.getConstant(); - if (constant == null) - continue next_instr; - for (int i=1; i < constants.length; i++) { - if (constant.equals(constants[i])) - continue next_instr; - } - if (constant instanceof Integer) { - int value = ((Integer) constant).intValue(); - if (value >= Short.MIN_VALUE - && value <= Short.MAX_VALUE) - continue next_instr; - } - gcp.reserveConstant(constant); - } - } - } - - public void prepareWriting(GrowableConstantPool gcp) { - /* Recalculate addr, length and add all constants to gcp */ - int addr = 0; - for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - int opcode = instr.getOpcode(); - instr.setAddr(addr); - int length; - switch_opc: - switch (opcode) { - case opc_ldc: - case opc_ldc2_w: { - Object constant = instr.getConstant(); - if (constant == null) { - length = 1; - break switch_opc; - } - for (int i=1; i < constants.length; i++) { - if (constant.equals(constants[i])) { - length = 1; - break switch_opc; - } - } - if (opcode == opc_ldc2_w) { - gcp.putLongConstant(constant); - length = 3; - break switch_opc; - } - if (constant instanceof Integer) { - int value = ((Integer) constant).intValue(); - if (value >= Byte.MIN_VALUE - && value <= Byte.MAX_VALUE) { - length = 2; - break switch_opc; - } else if (value >= Short.MIN_VALUE - && value <= Short.MAX_VALUE) { - length = 3; - break switch_opc; - } - } - if (gcp.putConstant(constant) < 256) { - length = 2; - } else { - length = 3; - } - break; - } - case opc_iinc: { - int slot = instr.getLocalSlot(); - int increment = instr.getIncrement(); - if (slot < 256 - && increment >= Byte.MIN_VALUE - && increment <= Byte.MAX_VALUE) - length = 3; - else - length = 6; - break; - } - case opc_iload: case opc_lload: - case opc_fload: case opc_dload: case opc_aload: - case opc_istore: case opc_lstore: - case opc_fstore: case opc_dstore: case opc_astore: - if (instr.getLocalSlot() < 4) { - length = 1; - break; - } - /* fall through */ - case opc_ret: { - if (instr.getLocalSlot() < 256) - length = 2; - else - length = 4; - break; - } - case opc_lookupswitch: { - length = 3-(addr % 4); - int[] values = instr.getValues(); - int npairs = values.length; - if (npairs > 0) { - int tablesize = values[npairs-1] - values[0] + 1; - if (4 + tablesize * 4 < 8 * npairs) { - // Use a table switch - length += 13 + 4 * tablesize; - break; - } - } - // Use a lookup switch - length += 9 + 8 * npairs; - break; - } - case opc_goto: case opc_jsr: { - int dist = instr.getSingleSucc().getAddr() - instr.getAddr(); - if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) { - /* wide goto / jsr */ - length = 5; - break; - } - /* fall through */ - } - case opc_ifeq: case opc_ifne: - case opc_iflt: case opc_ifge: - case opc_ifgt: case opc_ifle: - case opc_if_icmpeq: case opc_if_icmpne: - case opc_if_icmplt: case opc_if_icmpge: - case opc_if_icmpgt: case opc_if_icmple: - case opc_if_acmpeq: case opc_if_acmpne: - case opc_ifnull: case opc_ifnonnull: - length = 3; - break; - case opc_multianewarray: { - if (instr.getDimensions() == 1) { - String clazz = instr.getClazzType().substring(1); - if (newArrayTypes.indexOf(clazz.charAt(0)) != -1) { - length = 2; - } else { - gcp.putClassType(clazz); - length = 3; - } - } else { - gcp.putClassType(instr.getClazzType()); - length = 4; - } - break; - } - case opc_getstatic: - case opc_getfield: - case opc_putstatic: - case opc_putfield: - gcp.putRef(gcp.FIELDREF, instr.getReference()); - length = 3; - break; - case opc_invokespecial: - case opc_invokestatic: - case opc_invokevirtual: - gcp.putRef(gcp.METHODREF, instr.getReference()); - length = 3; - break; - case opc_invokeinterface: - gcp.putRef(gcp.INTERFACEMETHODREF, instr.getReference()); - length = 5; - break; - case opc_new: - case opc_checkcast: - case opc_instanceof: - gcp.putClassType(instr.getClazzType()); - length = 3; - break; - case opc_nop: - case opc_iaload: case opc_laload: case opc_faload: - case opc_daload: case opc_aaload: - case opc_baload: case opc_caload: case opc_saload: - case opc_iastore: case opc_lastore: case opc_fastore: - case opc_dastore: case opc_aastore: - case opc_bastore: case opc_castore: case opc_sastore: - case opc_pop: case opc_pop2: - case opc_dup: case opc_dup_x1: case opc_dup_x2: - case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: - case opc_swap: - case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: - case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: - case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: - case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: - case opc_irem: case opc_lrem: case opc_frem: case opc_drem: - case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: - case opc_ishl: case opc_lshl: - case opc_ishr: case opc_lshr: - case opc_iushr: case opc_lushr: - case opc_iand: case opc_land: - case opc_ior: case opc_lor: - case opc_ixor: case opc_lxor: - case opc_i2l: case opc_i2f: case opc_i2d: - case opc_l2i: case opc_l2f: case opc_l2d: - case opc_f2i: case opc_f2l: case opc_f2d: - case opc_d2i: case opc_d2l: case opc_d2f: - case opc_i2b: case opc_i2c: case opc_i2s: - case opc_lcmp: case opc_fcmpl: case opc_fcmpg: - case opc_dcmpl: case opc_dcmpg: - case opc_ireturn: case opc_lreturn: - case opc_freturn: case opc_dreturn: case opc_areturn: - case opc_return: - case opc_athrow: - case opc_arraylength: - case opc_monitorenter: case opc_monitorexit: - length = 1; - break; - default: - throw new ClassFormatError("Invalid opcode "+opcode); - } - addr += length; - } - instructions.setLastAddr(addr); - for (int i=0; i< exceptionHandlers.length; i++) - if (exceptionHandlers[i].type != null) - gcp.putClassName(exceptionHandlers[i].type); - if (lvt != null) { - gcp.putUTF8("LocalVariableTable"); - for (int i=0; i < lvt.length; i++) { - gcp.putUTF8(lvt[i].name); - gcp.putUTF8(lvt[i].type); - } - } - if (lnt != null) - gcp.putUTF8("LineNumberTable"); - prepareAttributes(gcp); - } - - protected int getKnownAttributeCount() { - int count = 0; - if (lvt != null) - count++; - if (lnt != null) - count++; - return count; - } - - public void writeKnownAttributes(GrowableConstantPool gcp, - DataOutputStream output) - throws IOException { - if (lvt != null) { - output.writeShort(gcp.putUTF8("LocalVariableTable")); - int count = lvt.length; - int length = 2 + 10 * count; - output.writeInt(length); - output.writeShort(count); - for (int i=0; i < count; i++) { - output.writeShort(lvt[i].start.getAddr()); - output.writeShort(lvt[i].end.getAddr() + lvt[i].end.getLength() - - lvt[i].start.getAddr()); - output.writeShort(gcp.putUTF8(lvt[i].name)); - output.writeShort(gcp.putUTF8(lvt[i].type)); - output.writeShort(lvt[i].slot); - } - } - if (lnt != null) { - output.writeShort(gcp.putUTF8("LineNumberTable")); - int count = lnt.length; - int length = 2 + 4 * count; - output.writeInt(length); - output.writeShort(count); - for (int i=0; i < count; i++) { - output.writeShort(lnt[i].start.getAddr()); - output.writeShort(lnt[i].linenr); - } - } - } - - public void write(GrowableConstantPool gcp, - DataOutputStream output) throws IOException { - output.writeShort(maxStack); - output.writeShort(maxLocals); - output.writeInt(instructions.getCodeLength()); - for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { - Instruction instr = (Instruction) iter.next(); - int opcode = instr.getOpcode(); - switch_opc: - switch (opcode) { - case opc_iload: case opc_lload: - case opc_fload: case opc_dload: case opc_aload: - case opc_istore: case opc_lstore: - case opc_fstore: case opc_dstore: case opc_astore: { - int slot = instr.getLocalSlot(); - if (slot < 4) { - if (opcode < opc_istore) - output.writeByte(opc_iload_0 - + 4*(opcode-opc_iload) - + slot); - else - output.writeByte(opc_istore_0 - + 4*(opcode-opc_istore) - + slot); - } else if (slot < 256) { - output.writeByte(opcode); - output.writeByte(slot); - } else { - output.writeByte(opc_wide); - output.writeByte(opcode); - output.writeShort(slot); - } - break; - } - case opc_ret: { - int slot = instr.getLocalSlot(); - if (slot < 256) { - output.writeByte(opcode); - output.writeByte(slot); - } else { - output.writeByte(opc_wide); - output.writeByte(opcode); - output.writeShort(slot); - } - break; - } - case opc_ldc: - case opc_ldc2_w: { - Object constant = instr.getConstant(); - if (constant == null) { - output.writeByte(opc_aconst_null); - break switch_opc; - } - for (int i=1; i < constants.length; i++) { - if (constant.equals(constants[i])) { - output.writeByte(opc_aconst_null + i); - break switch_opc; - } - } - if (opcode == opc_ldc2_w) { - output.writeByte(opcode); - output.writeShort(gcp.putLongConstant(constant)); - } else { - if (constant instanceof Integer) { - int value = ((Integer) constant).intValue(); - if (value >= Byte.MIN_VALUE - && value <= Byte.MAX_VALUE) { - - output.writeByte(opc_bipush); - output.writeByte(((Integer)constant) - .intValue()); - break switch_opc; - } else if (value >= Short.MIN_VALUE - && value <= Short.MAX_VALUE) { - output.writeByte(opc_sipush); - output.writeShort(((Integer)constant) - .intValue()); - break switch_opc; - } - } - if (instr.getLength() == 2) { - output.writeByte(opc_ldc); - output.writeByte(gcp.putConstant(constant)); - } else { - output.writeByte(opc_ldc_w); - output.writeShort(gcp.putConstant(constant)); - } - } - break; - } - case opc_iinc: { - int slot = instr.getLocalSlot(); - int incr = instr.getIncrement(); - if (instr.getLength() == 3) { - output.writeByte(opcode); - output.writeByte(slot); - output.writeByte(incr); - } else { - output.writeByte(opc_wide); - output.writeByte(opcode); - output.writeShort(slot); - output.writeShort(incr); - } - break; - } - case opc_goto: - case opc_jsr: - if (instr.getLength() == 5) { - /* wide goto or jsr */ - output.writeByte(opcode + (opc_goto_w - opc_goto)); - output.writeInt(instr.getSingleSucc().getAddr() - - instr.getAddr()); - break; - } - /* fall through */ - case opc_ifeq: case opc_ifne: - case opc_iflt: case opc_ifge: - case opc_ifgt: case opc_ifle: - case opc_if_icmpeq: case opc_if_icmpne: - case opc_if_icmplt: case opc_if_icmpge: - case opc_if_icmpgt: case opc_if_icmple: - case opc_if_acmpeq: case opc_if_acmpne: - case opc_ifnull: case opc_ifnonnull: - output.writeByte(opcode); - output.writeShort(instr.getSingleSucc().getAddr() - - instr.getAddr()); - break; - - case opc_lookupswitch: { - int align = 3-(instr.getAddr() % 4); - int[] values = instr.getValues(); - int npairs = values.length; - int defAddr = instr.getSuccs()[npairs].getAddr() - - instr.getAddr(); - - if (npairs > 0) { - int tablesize = values[npairs-1] - values[0] + 1; - if (4 + tablesize * 4 < 8 * npairs) { - // Use a table switch - output.writeByte(opc_tableswitch); - output.write(new byte[align]); - /* def */ - output.writeInt(defAddr); - /* low */ - output.writeInt(values[0]); - /* high */ - output.writeInt(values[npairs-1]); - int pos = values[0]; - for (int i = 0; i < npairs; i++) { - while (pos++ < values[i]) - output.writeInt(defAddr); - output.writeInt(instr.getSuccs()[i].getAddr() - - instr.getAddr()); - } - break; - } - } - // Use a lookup switch - output.writeByte(opc_lookupswitch); - output.write(new byte[align]); - /* def */ - output.writeInt(defAddr); - output.writeInt(npairs); - for (int i=0; i < npairs; i++) { - output.writeInt(values[i]); - output.writeInt(instr.getSuccs()[i].getAddr() - -instr.getAddr()); - } - break; - } - - case opc_getstatic: - case opc_getfield: - case opc_putstatic: - case opc_putfield: - output.writeByte(opcode); - output.writeShort(gcp.putRef(gcp.FIELDREF, - instr.getReference())); - break; - - case opc_invokespecial: - case opc_invokestatic: - case opc_invokeinterface: - case opc_invokevirtual: { - Reference ref = instr.getReference(); - output.writeByte(opcode); - if (opcode == opc_invokeinterface) { - output.writeShort(gcp.putRef(gcp.INTERFACEMETHODREF, ref)); - output.writeByte - (TypeSignature.getArgumentSize(ref.getType()) + 1); - output.writeByte(0); - } else - output.writeShort(gcp.putRef(gcp.METHODREF, ref)); - break; - } - case opc_new: - case opc_checkcast: - case opc_instanceof: - output.writeByte(opcode); - output.writeShort(gcp.putClassType(instr.getClazzType())); - break; - case opc_multianewarray: - if (instr.getDimensions() == 1) { - String clazz = instr.getClazzType().substring(1); - int index = newArrayTypes.indexOf(clazz.charAt(0)); - if (index != -1) { - output.writeByte(opc_newarray); - output.writeByte(index + 4); - } else { - output.writeByte(opc_anewarray); - output.writeShort(gcp.putClassType(clazz)); - } - } else { - output.writeByte(opcode); - output.writeShort(gcp.putClassType(instr.getClazzType())); - output.writeByte(instr.getDimensions()); - } - break; - - case opc_nop: - case opc_iaload: case opc_laload: case opc_faload: - case opc_daload: case opc_aaload: - case opc_baload: case opc_caload: case opc_saload: - case opc_iastore: case opc_lastore: case opc_fastore: - case opc_dastore: case opc_aastore: - case opc_bastore: case opc_castore: case opc_sastore: - case opc_pop: case opc_pop2: - case opc_dup: case opc_dup_x1: case opc_dup_x2: - case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: - case opc_swap: - case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: - case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: - case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: - case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: - case opc_irem: case opc_lrem: case opc_frem: case opc_drem: - case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: - case opc_ishl: case opc_lshl: - case opc_ishr: case opc_lshr: - case opc_iushr: case opc_lushr: - case opc_iand: case opc_land: - case opc_ior: case opc_lor: - case opc_ixor: case opc_lxor: - case opc_i2l: case opc_i2f: case opc_i2d: - case opc_l2i: case opc_l2f: case opc_l2d: - case opc_f2i: case opc_f2l: case opc_f2d: - case opc_d2i: case opc_d2l: case opc_d2f: - case opc_i2b: case opc_i2c: case opc_i2s: - case opc_lcmp: case opc_fcmpl: case opc_fcmpg: - case opc_dcmpl: case opc_dcmpg: - case opc_ireturn: case opc_lreturn: - case opc_freturn: case opc_dreturn: case opc_areturn: - case opc_return: - case opc_athrow: - case opc_arraylength: - case opc_monitorenter: case opc_monitorexit: - output.writeByte(opcode); - break; - default: - throw new ClassFormatError("Invalid opcode "+opcode); - } - } - - output.writeShort(exceptionHandlers.length); - for (int i=0; i< exceptionHandlers.length; i++) { - output.writeShort(exceptionHandlers[i].start.getAddr()); - output.writeShort(exceptionHandlers[i].end.getNextByAddr().getAddr()); - output.writeShort(exceptionHandlers[i].catcher.getAddr()); - output.writeShort((exceptionHandlers[i].type == null) ? 0 - : gcp.putClassName(exceptionHandlers[i].type)); - } - writeAttributes(gcp, output); - } - - public void dropInfo(int howMuch) { - if ((howMuch & KNOWNATTRIBS) != 0) { - lvt = null; - lnt = null; - } - super.dropInfo(howMuch); - } - - public int getSize() { - /* maxStack: 2 - * maxLocals: 2 - * code: 4 + codeLength - * exc count: 2 - * exceptions: n * 8 - * attributes: - * lvt_name: 2 - * lvt_length: 4 - * lvt_count: 2 - * lvt_entries: n * 10 - * attributes: - * lnt_name: 2 - * lnt_length: 4 - * lnt_count: 2 - * lnt_entries: n * 4 - */ - int size = 0; - if (lvt != null) - size += 8 + lvt.length * 10; - if (lnt != null) - size += 8 + lnt.length * 4; - return 10 + instructions.getCodeLength() - + exceptionHandlers.length * 8 - + getAttributeSize() + size; - } - - public int getMaxStack() { - return maxStack; - } - - public int getMaxLocals() { - return maxLocals; - } - - public MethodInfo getMethodInfo() { - return methodInfo; - } - - public List getInstructions() { - return instructions; - } - - public Handler[] getExceptionHandlers() { - return exceptionHandlers; - } - - public LocalVariableInfo[] getLocalVariableTable() { - return lvt; - } - - public LineNumber[] getLineNumberTable() { - return lnt; - } - - public void setMaxStack(int ms) { - maxStack = ms; - } - - public void setMaxLocals(int ml) { - maxLocals = ml; - } - - public void setExceptionHandlers(Handler[] handlers) { - exceptionHandlers = handlers; - } - - public void setLocalVariableTable(LocalVariableInfo[] newLvt) { - lvt = newLvt; - } - - public void setLineNumberTable(LineNumber[] newLnt) { - lnt = newLnt; - } - - public String toString() { - return "Bytecode "+methodInfo; - } -} diff --git a/jode/jode/bytecode/ClassFormatException.java b/jode/jode/bytecode/ClassFormatException.java index cfbc987..d262200 100644 --- a/jode/jode/bytecode/ClassFormatException.java +++ b/jode/jode/bytecode/ClassFormatException.java @@ -20,13 +20,23 @@ package jode.bytecode; /** - * This exception is thrown, if the class file has an unknown format. + * Thrown when a class file with an unknown or illegal format is loaded. + * * @author Jochen Hoenicke */ public class ClassFormatException extends java.io.IOException{ + /** + * Constructs a new class format exception with the given detail + * message. + * @param detail the detail message. + */ public ClassFormatException(String detail) { super(detail); } + + /** + * Constructs a new class format exception. + */ public ClassFormatException() { super(); } diff --git a/jode/jode/bytecode/ClassInfo.java b/jode/jode/bytecode/ClassInfo.java new file mode 100644 index 0000000..95ec702 --- /dev/null +++ b/jode/jode/bytecode/ClassInfo.java @@ -0,0 +1,1469 @@ +/* ClassInfo Copyright (C) 1998-1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; +import jode.GlobalOptions; +import jode.util.UnifyHash; + +import java.io.DataInputStream; +import java.io.BufferedInputStream; +import java.io.DataOutputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.util.Enumeration; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +///#def COLLECTIONS java.util +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; +///#enddef +///#def COLLECTIONEXTRA java.lang +import java.lang.Comparable; +///#enddef + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Accesses, creates or modifies java bytecode classes or interfaces. + * This class represents a class or interface, it can't be used for + * primitive or array types. Every class/interface is associated with + * a class path, which is used to load the class in memory. + * + *

Creating a class

+ * You create a new ClassInfo, by calling {@link + * ClassPath#getClassInfo}. The resulting ClassInfo is empty and you + * now have two different possibilities to fill it with informations: + * You load the class from its classpath (from which it was created) + * or you build it from scratch by setting its contents with the + * various setXXX methods. + * + *

Changing a class

+ * Even if the class is already filled with information you can change + * it. You can, for example, set another array of methods, change the + * modifiers, or rename the class. Use the various + * setXXX methods. + * + *

The components of a class

+ * A class consists of several components: + *
+ *
name
+ * The name of the class. The name is already set, when you create + * a new ClassInfo with getClassInfo. If you change this name this + * has some consequences, read the description of the {@link + * #setName} method. + *
+ *
class name
+ * The short java name of this class, i.e. the name that appears + * behind the "class" keyword in the java source file. While + * getClassName() also works for top level classes, + * setClassName() must only be called on inner classes and will not + * change the bytecode name.
+ * + * E.g.: The ClassName of java.util.Map$Entry is + * Entry. If you change its ClassName to + * Yrtne and save it, it will still be in a file called + * Map$Entry.class, but a debugger would call it + * java.util.Map.Yrtne. Note that you should also save + * Map, because it also has a reference to the + * ClassName. + *
+ *
modifiers
+ * There is a set of access modifiers (AKA access flags) attached to + * each class. They are represented as integers (bitboard) and can + * be conveniently accessed via + * java.lang.reflect.Modifier.

+ * + * Inner classes can have more modifiers than normal classes. To be + * backwards compatible this was implemented by Sun by having the real + * modifiers for inner classes at a special location, while the old + * location has only the modifiers that were allowed previously. + * This package knows about this and always returns the real modifiers. + * The old modifiers are checked if they match the new extended ones.
+ * TODO: Check that reflection returns the new modifiers! + *
+ *
superclass
+ * Every class except java.lang.Object has a super class. The super class + * is created in the same classpath as the current class. Interfaces + * always have java.lang.Object as their super class. + *
+ *
interfaces
+ * Every class (resp. interfaces) can implement (resp. extend) + * zero or more interfaces. + *
+ *
fields
+ * Fields are represented as {@link FieldInfo} objects. + *
+ *
methods
+ * Fields are represented as {@link MethodInfo} objects. + *
+ *
method scoped
+ * True if this class is an anonymous or method scoped class. + *
+ *
outer class
+ * the class of which this class is the inner. It returns null for + * top level classes and for method scoped classes.
+ *
+ *
classes
+ * the inner classes which is an array of ClassInfo. This doesn't + * include method scoped classes.
+ *
+ *
extra classes
+ * this field tells only, for which additional classes the class + * files contains an InnerClass Attribute. This can be the method + * scoped classes or the inner inner classes, but there is no + * promise.
+ *
+ *
source file
+ * The name of source file. The JVM uses this field when a stack + * trace is produced. + *
+ *
+ * + *

inner classes

+ * Inner classes are supported as far as the information is present in + * the bytecode. But you can always ignore this inner information, + * and access inner classes by their bytecode name, + * e.g. java.util.Map$Entry. There are four different types + * of classes: + *
+ *
normal package scoped classes
+ * A class is package scoped if, and only if + * getOuterClass() returns null and + * isMethodScoped() returns false. + *
+ *
inner class scoped classes
+ * A class is class scoped if, and only if + * getOuterClass() returns not null. + * + * The bytecode name (getName()) of an inner class is + * most times of the form Package.Outer$Inner. But + * ClassInfo also supports differently named classes, as long as the + * InnerClass attribute is present. The method + * getClassName() returns the name of the inner class + * (Inner in the above example). + * + * You can get all inner classes of a class with the + * getClasses method. + *
+ *
named method scoped classes
+ * A class is a named method scoped class if, and only if + * isMethodScoped() returns true and + * getClassName() returns not null. In + * that case getOuterClass() returns null, + * too.

+ * + * The bytecode name (getName()) of an method scoped class is + * most times of the form Package.Outer$Number$Inner. But + * ClassInfo also supports differently named classes, as long as the + * InnerClass attribute is present.

+ * + * There's no way to get the method scoped classes of a method, except + * by analyzing its instructions. + *
+ *
anonymous classes
+ * A class is an anonymous class if, and only if + * isMethodScoped() returns true and + * getClassName() returns null. In that + * case getOuterClass() returns null, + * too.

+ * + * The bytecode name (getName()) of an method scoped class is + * most times of the form Package.Outer$Number. But + * ClassInfo also supports differently named classes, as long as the + * InnerClass attribute is present.

+ * + * There's no way to get the anonymous classes of a method, except + * by analyzing its instructions. + *
+ *
+ * + *
+ *

open questions...

+ * + * I represent most types as java/lang/String (type + * signatures); this is convenient since java bytecode does the same. + * On the other hand a class type should be represented as + * jode/bytecode/ClassInfo class. There should be a + * method to convert to it, but I need a ClassPath for this. Should + * the method be in ClassInfo (I don't think so), should an instance + * of TypeSignature have a ClassPath as member variable, or should + * getClassInfo() take a ClassPath parameter? What about arrays? + *
+ * + * Should load(HIERARCHY) also try to load hierarchy of super class. + * If yes, should it return an IOException if super class can't be + * found? Or should getSuperclass throw the IOException + * instead (I don't like that).
+ * Current Solution: superClassOf and + * implementedBy can throw an IOException, getSuperclass not. + * + * @author Jochen Hoenicke */ +public final class ClassInfo extends BinaryInfo implements Comparable { + + private static ClassPath defaultClasspath; + + private int status = 0; + + private boolean modified = false; + private ClassPath classpath; + + private int modifiers = -1; + private String name; + private String className; + private boolean methodScoped; + private ClassInfo superclass; + private ClassInfo outerClass; + private ClassInfo[] interfaces; + private ClassInfo[] innerClasses; + private ClassInfo[] extraClasses; + private FieldInfo[] fields; + private MethodInfo[] methods; + private String sourceFile; + + /** + * This constant can be used as parameter to drop. It specifies + * that no information at all should be kept for the current class. + * + * @see #load + */ + public static final int NONE = 0; + /** + * This constant can be used as parameter to load. It specifies + * that at least the outer class information should be loaded, i.e. + * the outer class, the class name. It is the + * only information that is loaded recursively: It is also + * automatically loaded for the outer class and it is loaded for + * all inner and extra classes, if these fields are loaded. + * The reason for the recursive load is simple: In java bytecode + * a class contains the outer class information for all outer, + * inner and extra classes, so we can create this information + * without the need to read the outer class. We also need this + * information for outer and inner classes when writing a class. + * + * @see #load + */ + public static final int OUTERCLASS = 5; + /** + * This constant can be used as parameter to load. It specifies + * that at least the hierarchy information, i.e. the + * superclass/interfaces fields and the modifiers + * of this class should be loaded. + * + * @see #load + */ + public static final int HIERARCHY = 10; + /** + * This constant can be used as parameter to load. It specifies + * that all public fields, methods and inner class declarations + * should be loaded. It doesn't load method bodies. + * + * @see #load + */ + public static final int PUBLICDECLARATIONS = 20; + /** + * This constant can be used as parameter to load. It specifies + * that all the fields, methods and inner class declaration + * should be loaded. It doesn't load method bodies. + * + * @see #load + */ + public static final int DECLARATIONS = 30; + /** + * This constant can be used as parameter to load. It specifies + * that everything in the class except non-standard attributes + * should be loaded. + * + * @see #load + */ + public static final int ALMOSTALL = 90; + /** + * This constant can be used as parameter to load. It specifies + * that everything in the class should be loaded. + * + * @see #load + */ + public static final int ALL = 100; + + /** + * @deprecated + */ + public static void setClassPath(String path) { + setClassPath(new ClassPath(path)); + } + + /** + * @deprecated + */ + public static void setClassPath(ClassPath path) { + defaultClasspath= path; + } + + /** + * @deprecated + */ + public static boolean exists(String name) { + return defaultClasspath.existsClass(name); + } + + /** + * @deprecated + */ + public static boolean isPackage(String name) { + return defaultClasspath.isDirectory(name.replace('.', '/')); + } + + /** + * @deprecated + */ + public static Enumeration getClassesAndPackages(String packageName) { + return defaultClasspath.listClassesAndPackages(packageName); + } + + /** + * @deprecated + */ + public static ClassInfo forName(String name) { + return defaultClasspath.getClassInfo(name); + } + + ClassInfo(String name, ClassPath classpath) { + this.name = name.intern(); + this.classpath = classpath; + } + + /** + * Returns the classpath in which this class was created. + */ + public ClassPath getClassPath() { + return classpath; + } + + /****** READING CLASS FILES ***************************************/ + + private static int javaModifiersToBytecode(int javaModifiers) { + int modifiers = javaModifiers & (Modifier.FINAL + | 0x20 /*ACC_SUPER*/ + | Modifier.INTERFACE + | Modifier.ABSTRACT); + + if ((javaModifiers & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) + return Modifier.PUBLIC | modifiers; + else + return modifiers; + } + + private void mergeModifiers(int newModifiers) { + if (modifiers == -1) { + modifiers = newModifiers; + return; + } + if (((modifiers ^ newModifiers) & ~0x20) == 0) { + modifiers |= newModifiers; + return; + } + + int oldSimple = javaModifiersToBytecode(modifiers); + if (((oldSimple ^ newModifiers) & ~0x20) == 0) { + modifiers = newModifiers | (modifiers & 0x20); + return; + } + + int newSimple = javaModifiersToBytecode(newModifiers); + if (((newSimple ^ modifiers) & ~0x20) == 0) { + modifiers |= newModifiers & 0x20; + return; + } + + throw new ClassFormatError + ("modifiers in InnerClass info doesn't match: " + + modifiers + "<->" + newModifiers); + } + + private void mergeOuterInfo(String className, ClassInfo outer, + int realModifiers, boolean ms) + throws ClassFormatException + { + if (status >= OUTERCLASS) { + if ((className == null + ? this.className != null : !className.equals(this.className)) + || this.outerClass != outer) + throw new ClassFormatException("Outer information mismatch " + +name+": "+className+","+outer+","+ms+"<->"+this.className +","+this.outerClass+","+this.methodScoped); + mergeModifiers(realModifiers); + } else { + if (realModifiers != -1) + mergeModifiers(realModifiers); + this.className = className; + this.outerClass = outer; + this.methodScoped = ms; + this.status = OUTERCLASS; + } + } + + private void readInnerClassesAttribute(int length, ConstantPool cp, + DataInputStream input) + throws IOException + { + /* The InnerClasses attribute is transformed in a special way + * so we want to taker a closer look. According to the inner + * class specification, + * + * http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814 + * + * there are several InnerClass records in a class. We differ + * three different types: + * + * The InnerClass records for our own inner classes: They can + * easily be recognized, since this class must be mentioned in + * the outer_class_info_index field. + * + * The InnerClass records for the outer class and its outer + * classes, and so on: According to the spec, these must + * always be present if this is a class scoped class. And they + * must be in reversed order, i.e. the outer most class comes + * first. + * + * Some other InnerClass records, the extra classes. This is + * optional, but we don't want to loose this information if we + * just transform classes, so we memorize for which classes we + * have to keep the inforamtion anyway. + * + * Currently we don't use all informations, since we don't + * update the information for inner/outer/extra classes or + * check it for consistency. This might be bad, since this + * means that + *
+	 * load(ALL); write(new File())
+	 * 
+ * doesn't work. + */ + + int count = input.readUnsignedShort(); + if (length != 2 + 8 * count) + throw new ClassFormatException + ("InnerClasses attribute has wrong length"); + + int innerCount = 0, outerCount = 0, extraCount = 0; + /** + * The first part will contain the inner classes, the last + * part the extra classes. + */ + ClassInfo[] innerExtra = new ClassInfo[count]; + + for (int i=0; i < count; i++) { + int innerIndex = input.readUnsignedShort(); + int outerIndex = input.readUnsignedShort(); + int nameIndex = input.readUnsignedShort(); + String inner = cp.getClassName(innerIndex); + String outer = outerIndex != 0 + ? cp.getClassName(outerIndex) : null; + String innername = nameIndex != 0 ? cp.getUTF8(nameIndex) : null; + int access = input.readUnsignedShort(); + if (innername != null && innername.length() == 0) + innername = null; + + ClassInfo innerCI = classpath.getClassInfo(inner); + ClassInfo outerCI = null; + if (outer != null) { + outerCI = classpath.getClassInfo(outer); + /* If we didn't find an InnerClasses info for outerCI, yet, + * this means that it doesn't have an outer class. So we + * know its (empty) outer class status now. + */ + if (outerCI.status < OUTERCLASS) + outerCI.mergeOuterInfo(null, null, -1, false); + } + + if (innername == null) + /* anonymous class */ + outerCI = null; + + innerCI.mergeOuterInfo(innername, outerCI, + access, outerCI == null); + if (outerCI == this) + innerExtra[innerCount++] = innerCI; + else { + /* Remove outerCI from the extra part of innerExtra + * since it is implicit now. + */ + for (int j = count - 1; j >= count - extraCount; j--) { + if (innerExtra[j] == outerCI) { + System.arraycopy(innerExtra, count - extraCount, + innerExtra, count - extraCount + 1, + j - (count - extraCount)); + extraCount--; + break; + } + } + + /* Add innerCI to the extra classes, except if it is + * this class. + */ + if (innerCI != this) + innerExtra[count - (++extraCount)] = innerCI; + } + } + + /* Now inner classes are at the front of the array in correct + * order. The extra classes are in reverse order at the end + * of the array. + */ + if (innerCount > 0) { + innerClasses = new ClassInfo[innerCount]; + for (int i=0; i < innerCount; i++) + innerClasses[i] = innerExtra[i]; + } else + innerClasses = null; + + if (extraCount > 0) { + extraClasses = new ClassInfo[extraCount]; + for (int i = 0; i < extraCount; i++) + extraClasses[i] = innerExtra[count - i - 1]; + } else + extraClasses = null; + } + + void readAttribute(String name, int length, + ConstantPool cp, + DataInputStream input, + int howMuch) throws IOException { + if (howMuch >= ClassInfo.ALMOSTALL && name.equals("SourceFile")) { + if (length != 2) + throw new ClassFormatException("SourceFile attribute" + + " has wrong length"); + sourceFile = cp.getUTF8(input.readUnsignedShort()); + } else if (howMuch >= ClassInfo.OUTERCLASS + && name.equals("InnerClasses")) { + readInnerClassesAttribute(length, cp, input); + } else + super.readAttribute(name, length, cp, input, howMuch); + } + + void loadFromReflection(Class clazz, int howMuch) + throws SecurityException, ClassFormatException { + if (howMuch >= OUTERCLASS) { + Class declarer = clazz.getDeclaringClass(); + if (declarer != null) { + /* We have to guess the className, since reflection doesn't + * tell it :-( + */ + int dollar = name.lastIndexOf('$'); + className = name.substring(dollar+1); + outerClass = classpath.getClassInfo(declarer.getName()); + /* As mentioned above OUTERCLASS is recursive */ + outerClass.loadFromReflection(declarer, OUTERCLASS); + } else { + /* Check if class name ends with $[numeric]$name or + * $[numeric], in which case it is a method scoped + * resp. anonymous class. + */ + int dollar = name.lastIndexOf('$'); + if (dollar >= 0 && Character.isDigit(name.charAt(dollar+1))) { + /* anonymous class */ + className = null; + outerClass = null; + methodScoped = true; + } else { + int dollar2 = name.lastIndexOf('$', dollar); + if (dollar2 >= 0 + && Character.isDigit(name.charAt(dollar2+1))) { + className = name.substring(dollar+1); + outerClass = null; + methodScoped = true; + } + } + } + + } + if (howMuch >= HIERARCHY) { + modifiers = clazz.getModifiers(); + if (clazz.getSuperclass() == null) + superclass = clazz == Object.class + ? null : classpath.getClassInfo("java.lang.Object"); + else + superclass = classpath.getClassInfo + (clazz.getSuperclass().getName()); + Class[] ifaces = clazz.getInterfaces(); + interfaces = new ClassInfo[ifaces.length]; + for (int i=0; i= PUBLICDECLARATIONS) { + Field[] fs; + Method[] ms; + Constructor[] cs; + Class[] is; + if (howMuch == PUBLICDECLARATIONS) { + fs = clazz.getFields(); + ms = clazz.getMethods(); + cs = clazz.getConstructors(); + is = clazz.getClasses(); + } else { + fs = clazz.getDeclaredFields(); + ms = clazz.getDeclaredMethods(); + cs = clazz.getDeclaredConstructors(); + is = clazz.getDeclaredClasses(); + } + + int len = 0; + for (int i = fs.length; --i >= 0; ) { + if (fs[i].getDeclaringClass() == clazz) + len++; + } + int fieldPtr = len; + fields = new FieldInfo[len]; + for (int i = fs.length; --i >= 0; ) { + if (fs[i].getDeclaringClass() == clazz) { + String type = TypeSignature.getSignature(fs[i].getType()); + fields[--fieldPtr] = new FieldInfo + (fs[i].getName(), type, fs[i].getModifiers()); + } + } + + len = cs.length; + for (int i = ms.length; --i >= 0; ) { + if (ms[i].getDeclaringClass() == clazz) + len++; + } + methods = new MethodInfo[len]; + int methodPtr = len; + for (int i = ms.length; --i >= 0; ) { + if (ms[i].getDeclaringClass() == clazz) { + String type = TypeSignature.getSignature + (ms[i].getParameterTypes(), ms[i].getReturnType()); + methods[--methodPtr] = new MethodInfo + (ms[i].getName(), type, ms[i].getModifiers()); + } + } + for (int i = cs.length; --i >= 0; ) { + String type = TypeSignature.getSignature + (cs[i].getParameterTypes(), void.class); + methods[--methodPtr] = new MethodInfo + ("", type, cs[i].getModifiers()); + } + if (is.length > 0) { + innerClasses = new ClassInfo[is.length]; + for (int i = is.length; --i >= 0; ) { + innerClasses[i] = classpath.getClassInfo(is[i].getName()); + /* As mentioned above OUTERCLASS is loaded recursive */ + innerClasses[i].loadFromReflection(is[i], OUTERCLASS); + } + } else + innerClasses = null; + } + status = howMuch; + } + + /** + * Reads a class file from a data input stream. You should really + * load a class from its classpath instead. This may + * be useful for special kinds of input streams, that ClassPath + * doesn't handle though. + * + * @param input The input stream, containing the class in standard + * bytecode format. + * @param howMuch The amount of information that should be read in, one + * of HIERARCHY, PUBLICDECLARATIONS, DECLARATIONS or ALL. + * @exception ClassFormatException if the file doesn't denote a valid + * class. + * @exception IOException if input throws an exception. + * @exception IllegalStateException if this ClassInfo was modified. + * @see #load + */ + public void read(DataInputStream input, int howMuch) + throws IOException + { + if (modified) + throw new IllegalStateException(name); + if (status >= howMuch) + return; + + /* Since we have to read the whole class anyway, we load all + * info, that we may need later and that does not take much memory. + */ + if (howMuch <= DECLARATIONS) + howMuch = DECLARATIONS; + + /* header */ + if (input.readInt() != 0xcafebabe) + throw new ClassFormatException("Wrong magic"); + if (input.readUnsignedShort() > 3) + throw new ClassFormatException("Wrong minor"); + if (input.readUnsignedShort() != 45) + throw new ClassFormatException("Wrong major"); + + /* constant pool */ + ConstantPool cpool = new ConstantPool(); + cpool.read(input); + + /* always read modifiers, name, super, ifaces */ + { + modifiers = input.readUnsignedShort(); + String className = cpool.getClassName(input.readUnsignedShort()); + if (!name.equals(className)) + throw new ClassFormatException("wrong name " + className); + String superName = cpool.getClassName(input.readUnsignedShort()); + superclass = superName != null ? classpath.getClassInfo(superName) : null; + int count = input.readUnsignedShort(); + interfaces = new ClassInfo[count]; + for (int i=0; i< count; i++) { + interfaces[i] = classpath.getClassInfo + (cpool.getClassName(input.readUnsignedShort())); + } + } + + /* fields */ + if (howMuch >= PUBLICDECLARATIONS) { + int count = input.readUnsignedShort(); + fields = new FieldInfo[count]; + for (int i=0; i< count; i++) { + fields[i] = new FieldInfo(); + fields[i].read(cpool, input, howMuch); + } + } else { + byte[] skipBuf = new byte[6]; + int count = input.readUnsignedShort(); + for (int i=0; i< count; i++) { + input.readFully(skipBuf); // modifier, name, type + skipAttributes(input); + } + } + + /* methods */ + if (howMuch >= PUBLICDECLARATIONS) { + int count = input.readUnsignedShort(); + methods = new MethodInfo[count]; + for (int i=0; i< count; i++) { + methods[i] = new MethodInfo(); + methods[i].read(cpool, input, howMuch); + } + } else { + byte[] skipBuf = new byte[6]; + int count = input.readUnsignedShort(); + for (int i=0; i< count; i++) { + input.readFully(skipBuf); // modifier, name, type + skipAttributes(input); + } + } + + /* attributes */ + readAttributes(cpool, input, howMuch); + status = howMuch; + } + + private void reserveSmallConstants(GrowableConstantPool gcp) { + for (int i=0; i < fields.length; i++) + fields[i].reserveSmallConstants(gcp); + + for (int i=0; i < methods.length; i++) + methods[i].reserveSmallConstants(gcp); + } + + /****** WRITING CLASS FILES ***************************************/ + + private void prepareWriting(GrowableConstantPool gcp) { + gcp.putClassName(name); + gcp.putClassName(superclass.getName()); + for (int i=0; i < interfaces.length; i++) + gcp.putClassName(interfaces[i].getName()); + + for (int i=0; i < fields.length; i++) + fields[i].prepareWriting(gcp); + + for (int i=0; i < methods.length; i++) + methods[i].prepareWriting(gcp); + + if (sourceFile != null) { + gcp.putUTF8("SourceFile"); + gcp.putUTF8(sourceFile); + } + if (outerClass != null || methodScoped + || innerClasses != null || extraClasses != null) { + gcp.putUTF8("InnerClasses"); + + ClassInfo outer = this; + while (outer.outerClass != null || outer.methodScoped) { + if (outer.status <= OUTERCLASS) + throw new IllegalStateException + (outer.name + "'s state is " + outer.status); + if (outer.className != null) + gcp.putClassName(outer.className); + if (outer.outerClass == null) + break; + gcp.putClassName(outer.outerClass.name); + outer = outer.outerClass; + } + int innerCount = innerClasses != null ? innerClasses.length : 0; + for (int i = innerCount; i-- > 0; i++) { + if (innerClasses[i].status <= OUTERCLASS) + throw new IllegalStateException + (innerClasses[i].name + "'s state is " + + innerClasses[i].status); + if (innerClasses[i].outerClass != this) + throw new IllegalStateException + (innerClasses[i].name + "'s outer is " + + innerClasses[i].outerClass.name); + + gcp.putClassName(innerClasses[i].name); + if (innerClasses[i].className != null) + gcp.putUTF8(innerClasses[i].className); + } + int extraCount = extraClasses != null ? extraClasses.length : 0; + for (int i=extraCount; i-- >= 0; ) { + if (extraClasses[i].status <= OUTERCLASS) + throw new IllegalStateException + (extraClasses[i].name + "'s state is " + + extraClasses[i].status); + gcp.putClassName(extraClasses[i].name); + if (extraClasses[i].outerClass != null) + gcp.putClassName(extraClasses[i].outerClass.name); + if (extraClasses[i].className != null) + gcp.putUTF8(extraClasses[i].className); + } + } + prepareAttributes(gcp); + } + + int getKnownAttributeCount() { + int count = 0; + if (sourceFile != null) + count++; + if (outerClass != null || methodScoped + || innerClasses != null || extraClasses != null) + count++; + return count; + } + + void writeKnownAttributes(GrowableConstantPool gcp, + DataOutputStream output) + throws IOException { + if (sourceFile != null) { + output.writeShort(gcp.putUTF8("SourceFile")); + output.writeInt(2); + output.writeShort(gcp.putUTF8(sourceFile)); + } + if (outerClass != null || methodScoped + || innerClasses != null || extraClasses != null) { + // XXX TODO: Closeness of extra outer information. + gcp.putUTF8("InnerClasses"); + + ClassInfo outer; + LinkedList outerExtraClasses = new LinkedList(); + + outer = this; + while (outer.outerClass != null || outer.methodScoped) { + /* Outers must be written in backward order, so we + * add them to the beginning of the list. + */ + outerExtraClasses.add(0, outer); + if (outer.outerClass == null) + break; + outer = outer.outerClass; + } + if (extraClasses != null) { + int extraCount = extraClasses.length; + for (int i = 0; i < extraCount; i++) { + outer = extraClasses[i]; + ListIterator insertIter + = outerExtraClasses.listIterator + (outerExtraClasses.size()); + int insertPos = outerExtraClasses.size(); + while (outer.outerClass != null || outer.methodScoped) { + if (outerExtraClasses.contains(outer)) + break; + /* We have to add outers in reverse order to the + * end of the list. We use the insertIter to do + * this trick. + */ + insertIter.add(outer); + insertIter.previous(); + if (outer.outerClass == null) + break; + outer = outer.outerClass; + } + } + } + + int innerCount = (innerClasses != null) ? innerClasses.length : 0; + int count = outerExtraClasses.size() + innerCount; + output.writeInt(2 + count * 8); + output.writeShort(count); + + for (Iterator i = outerExtraClasses.iterator(); i.hasNext(); ) { + outer = (ClassInfo) i.next(); + + output.writeShort(gcp.putClassName(outer.name)); + output.writeShort(outer.outerClass == null ? 0 : + gcp.putClassName(outer.outerClass.name)); + output.writeShort(outer.className == null ? 0 : + gcp.putUTF8(outer.className)); + output.writeShort(outer.modifiers); + } + for (int i = innerCount; i-- > 0; i++) { + output.writeShort(gcp.putClassName(innerClasses[i].name)); + output.writeShort(innerClasses[i].outerClass != null ? + gcp.putClassName(innerClasses[i] + .outerClass.name) : 0); + output.writeShort(innerClasses[i].className != null ? + gcp.putUTF8(innerClasses[i].className) : 0); + output.writeShort(innerClasses[i].modifiers); + } + } + } + + + /** + * Writes a class to the given DataOutputStream. Of course this only + * works if ALL information for this class is loaded/set. If this + * class has an outer class, inner classes or extra classes, their + * status must contain at least the OUTERCLASS information. + * @param out the output stream. + * @exception IOException if out throws io exception. + * @exception IllegalStateException if not enough information is set. + */ + public void write(DataOutputStream out) throws IOException { + if (status <= ALL) + throw new IllegalStateException("state is "+status); + + GrowableConstantPool gcp = new GrowableConstantPool(); + reserveSmallConstants(gcp); + prepareWriting(gcp); + + out.writeInt(0xcafebabe); + out.writeShort(3); + out.writeShort(45); + gcp.write(out); + + out.writeShort(javaModifiersToBytecode(modifiers)); + out.writeShort(gcp.putClassName(name)); + out.writeShort(gcp.putClassName(superclass.getName())); + out.writeShort(interfaces.length); + for (int i=0; i < interfaces.length; i++) + out.writeShort(gcp.putClassName(interfaces[i].getName())); + + out.writeShort(fields.length); + for (int i=0; i < fields.length; i++) + fields[i].write(gcp, out); + + out.writeShort(methods.length); + for (int i=0; i < methods.length; i++) + methods[i].write(gcp, out); + + writeAttributes(gcp, out); + } + + /** + * Loads the contents of a class from the classpath. + * @param howMuch The amount of information that should be read + * in, one of HIERARCHY, + * PUBLICDECLARATIONS, + * DECLARATIONS, ALMOSTALL + * or ALL. + * @exception ClassFormatException if the file doesn't denote a + * valid class. + * @exception FileNotFoundException if class wasn't found in classpath. + * @exception IOException if an io exception occured. + * @exception IllegalStateException if this ClassInfo was modified. + * @see #HIERARCHY + * @see #PUBLICDECLARATIONS + * @see #DECLARATIONS + * @see #ALMOSTALL + * @see #ALL + */ + public void load(int howMuch) + throws IOException + { + if (modified) + throw new IllegalStateException(name); + if (status >= howMuch) + return; + if (classpath.loadClass(this, howMuch)) + return; + throw new FileNotFoundException(name); + } + + /** + * Guess the contents of a class. It + * @param howMuch The amount of information that should be read, e.g. + * HIERARCHY. + * @exception ClassFormatException if the file doesn't denote a + * valid class. + * @exception FileNotFoundException if class wasn't found in classpath. + * @exception IOException if an io exception occured. + * @exception IllegalStateException if this ClassInfo was modified. + * @see #OUTERCLASS + * @see #HIERARCHY + * @see #PUBLICDECLARATIONS + * @see #DECLARATIONS + * @see #ALMOSTALL + * @see #ALL + */ + public void guess(int howMuch) + { + if (howMuch >= OUTERCLASS && status < OUTERCLASS) { + int dollar = name.lastIndexOf('$'); + if (dollar == -1) { + /* normal class */ + } else if (Character.isDigit(name.charAt(dollar+1))) { + /* anonymous class */ + modifiers = Modifier.PUBLIC | 0x20; + methodScoped = true; + } else { + modifiers = Modifier.PUBLIC | 0x20; + className = name.substring(dollar+1); + int prevDollar = name.lastIndexOf('$', dollar); + if (prevDollar >= 0 + && Character.isDigit(name.charAt(prevDollar))) { + /* probably method scoped class, (or inner class + * of anoymous class) */ + methodScoped = true; + outerClass = classpath.getClassInfo + (name.substring(0, prevDollar)); + } else { + /* inner class */ + modifiers = Modifier.PUBLIC | 0x20; + outerClass = classpath.getClassInfo + (name.substring(0, dollar)); + } + } + } + if (howMuch >= HIERARCHY && status < HIERARCHY) { + modifiers = Modifier.PUBLIC | 0x20; + if (name.equals("java.lang.Object")) + superclass = null; + else + superclass = classpath.getClassInfo("java.lang.Object"); + interfaces = new ClassInfo[0]; + } + if (howMuch >= PUBLICDECLARATIONS && status < PUBLICDECLARATIONS) { + methods = new MethodInfo[0]; + fields = new FieldInfo[0]; + innerClasses = new ClassInfo[0]; + } + status = howMuch; + } + + /** + * This is the counter part to load. It will drop all + * informations up keep and clean up the memory. + * @param keep tells how much info we should keep, can be + * NONE or anything that load accepts. + * @see #load + */ + public void drop(int keep) { + if (status <= keep) + return; + if (modified) { + System.err.println("Dropping info between " + keep + " and " + + status + " in modified class" + this + "."); + Thread.dumpStack(); + return; + } + if (keep < HIERARCHY) { + superclass = null; + interfaces = null; + } + + if (keep < OUTERCLASS) { + methodScoped = false; + outerClass = null; + innerClasses = null; + extraClasses = null; + } + + if (keep < PUBLICDECLARATIONS) { + fields = null; + methods = null; + status = keep; + } else { + if (keep < ALMOSTALL && status >= ALMOSTALL) { + for (int i=0; i < fields.length; i++) + fields[i].dropBody(); + for (int i=0; i < methods.length; i++) + methods[i].dropBody(); + } + if (status >= DECLARATIONS) + /* We don't drop non-public declarations, since this + * is not worth it. + */ + keep = DECLARATIONS; + } + + if (keep < ALMOSTALL && status >= ALMOSTALL) { + sourceFile = null; + super.dropAttributes(); + } + status = keep; + } + + /** + * Returns the full qualified name of this class. + * @return the full qualified name of this class, an interned string. + */ + public String getName() { + return name; + } + + /** + * Returns the java class name of a class, without package or + * outer classes. This is null for an anonymous class. For other + * classes it is the name that occured after the + * class keyword (provided it was compiled from + * java). + * This need OUTERCLASS information loaded to work properly. + * + * @return the short name of this class. Returns null for + * anonymous classes. + * + * @exception IllegalStateException if OUTERCLASS information wasn't + * loaded yet. */ + public String getClassName() { + if (status < OUTERCLASS) + throw new IllegalStateException("status is "+status); + if (className != null || isMethodScoped()) + return className; + + int dot = name.lastIndexOf('.'); + return name.substring(dot+1); + } + + /** + * Returns the ClassInfo object for the super class. + * @return the short name of this class. + * @exception IllegalStateException if HIERARCHY information wasn't + * loaded yet. + */ + public ClassInfo getSuperclass() { + if (status < HIERARCHY) + throw new IllegalStateException("status is "+status); + return superclass; + } + + /** + * Returns the ClassInfo object for the super class. + * @return the short name of this class. + * @exception IllegalStateException if HIERARCHY information wasn't + * loaded yet. + */ + public ClassInfo[] getInterfaces() { + if (status < HIERARCHY) + throw new IllegalStateException("status is "+status); + return interfaces; + } + + public int getModifiers() { + if (modifiers == -1) + throw new IllegalStateException("status is "+status); + return modifiers; + } + + public boolean isInterface() { + return Modifier.isInterface(getModifiers()); + } + + public FieldInfo findField(String name, String typeSig) { + if (status < PUBLICDECLARATIONS) + throw new IllegalStateException("status is "+status); + for (int i=0; i< fields.length; i++) + if (fields[i].getName().equals(name) + && fields[i].getType().equals(typeSig)) + return fields[i]; + return null; + } + + public MethodInfo findMethod(String name, String typeSig) { + if (status < PUBLICDECLARATIONS) + throw new IllegalStateException("status is "+status); + for (int i=0; i< methods.length; i++) + if (methods[i].getName().equals(name) + && methods[i].getType().equals(typeSig)) + return methods[i]; + return null; + } + + public MethodInfo[] getMethods() { + if (status < PUBLICDECLARATIONS) + throw new IllegalStateException("status is "+status); + return methods; + } + + public FieldInfo[] getFields() { + if (status < PUBLICDECLARATIONS) + throw new IllegalStateException("status is "+status); + return fields; + } + + /** + * Returns the outer class of this class if it is an inner class. + * This needs the OUTERCLASS information loaded. + * @return The class that declared this class, null if the class + * isn't declared in a class scope + * + * @exception IllegalStateException if OUTERCLASS information + * wasn't loaded yet. + */ + public ClassInfo getOuterClass() { + if (status < OUTERCLASS) + throw new IllegalStateException("status is "+status); + return outerClass; + } + + /** + * Returns true if the class was declared inside a method. + * This needs the OUTERCLASS information loaded. + * @return true if this is a method scoped or an anonymous class, + * false otherwise. + * + * @exception IllegalStateException if OUTERCLASS information + * wasn't loaded yet. + */ + public boolean isMethodScoped() { + if (status < OUTERCLASS) + throw new IllegalStateException("status is "+status); + return methodScoped; + } + + public ClassInfo[] getClasses() { + if (status < PUBLICDECLARATIONS) + throw new IllegalStateException("status is "+status); + return innerClasses; + } + + public ClassInfo[] getExtraClasses() { + if (status < OUTERCLASS) + throw new IllegalStateException("status is "+status); + return extraClasses; + } + + public String getSourceFile() { + return sourceFile; + } + + public void setName(String newName) { + name = newName.intern(); + modified = true; + } + + public void setSuperclass(ClassInfo newSuper) { + superclass = newSuper; + modified = true; + } + + public void setInterfaces(ClassInfo[] newIfaces) { + interfaces = newIfaces; + modified = true; + } + + public void setModifiers(int newModifiers) { + modifiers = newModifiers; + modified = true; + } + + public void setMethods(MethodInfo[] mi) { + methods = mi; + modified = true; + } + + public void setFields(FieldInfo[] fi) { + fields = fi; + modified = true; + } + + public void setOuterClass(ClassInfo oc) { + outerClass = oc; + modified = true; + } + + public void setMethodScoped(boolean ms) { + methodScoped = ms; + modified = true; + } + + public void setClasses(ClassInfo[] ic) { + innerClasses = ic; + modified = true; + } + + public void setExtraClasses(ClassInfo[] ec) { + extraClasses = ec; + modified = true; + } + + public void setSourceFile(String newSource) { + sourceFile = newSource; + modified = true; + } + + /** + * Gets the serial version UID of this class. If a final static + * long serialVersionUID field is present, its constant value + * is returned. Otherwise the UID is calculated with the algorithm + * in the serial version spec. + * @return the serial version UID of this class. + * @exception IllegalStateException if DECLARATIONS aren't loaded. + * @exception NoSuchAlgorithmException if SHA-1 message digest is not + * supported (needed for calculation of UID. + */ + public long getSerialVersionUID() throws NoSuchAlgorithmException { + if (status < DECLARATIONS) + throw new IllegalStateException("status is "+status); + FieldInfo fi = findField("serialVersionUID", "J"); + if (fi != null && fi.getConstant() != null) + /* We don't check for static final: if it has a constant it + * must be static final. + */ + return ((Long) fi.getConstant()).longValue(); + + final MessageDigest md = MessageDigest.getInstance("SHA"); + OutputStream digest = new OutputStream() { + + public void write(int b) { + md.update((byte) b); + } + + public void write(byte[] data, int offset, int length) { + md.update(data, offset, length); + } + }; + DataOutputStream out = new DataOutputStream(digest); + try { + out.writeUTF(this.name); + + // just look at interesting bits of modifiers + int modifs = javaModifiersToBytecode(this.modifiers) + & (Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC); + out.writeInt(modifs); + + ClassInfo[] interfaces = (ClassInfo[]) this.interfaces.clone(); + Arrays.sort(interfaces); + for (int i=0; i < interfaces.length; i++) + out.writeUTF(interfaces[i].name); + + FieldInfo[] fields = (FieldInfo[]) this.fields.clone(); + Arrays.sort(fields); + for (int i=0; i < fields.length; i++) { + modifs = fields[i].getModifiers(); + if ((modifs & Modifier.PRIVATE) != 0 + && (modifs & (Modifier.STATIC + | Modifier.TRANSIENT)) != 0) + continue; + + out.writeUTF(fields[i].getName()); + out.writeInt(modifs); + out.writeUTF(fields[i].getType()); + } + + FieldInfo[] methods = (FieldInfo[]) this.methods.clone(); + Arrays.sort(methods); + + for (int i=0; i < methods.length; i++) { + modifs = methods[i].getModifiers(); + if ((modifs & Modifier.PRIVATE) != 0) + continue; + + out.writeUTF(methods[i].getName()); + out.writeInt(modifs); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK. + out.writeUTF(methods[i].getType().replace('/', '.')); + } + + out.close(); + + byte[] sha = md.digest(); + long result = 0; + for (int i=0; i < 8; i++) { + result += (long)(sha[i] & 0xFF) << (8 * i); + } + return result; + } catch (IOException ex) { + /* Can't happen, since our OutputStream can't throw an + * IOException. + */ + throw new InternalError(); + } + } + + /** + * Compares two ClassInfo objects for name order. + * @return a positive number if this name lexicographically + * follows than other's name, a negative number if it preceeds the + * other, 0 if they are equal. + * @exception ClassCastException if other is not a ClassInfo. + */ + public int compareTo(Object other) { + return name.compareTo(((ClassInfo) other).name); + } + + /** + * Checks if this class is a super class of child. This loads the + * complete hierarchy of child on demand and can throw an IOException + * if some classes are not found or broken. + * @param child the class that should be a child class of us. + * @return true if this is as super class of child, false otherwise + * @exception IOException if hierarchy of child could not be loaded. + */ + public boolean superClassOf(ClassInfo child) throws IOException { + while (child != this && child != null) { + if (child.status < HIERARCHY) + child.load(HIERARCHY); + child = child.getSuperclass(); + } + return child == this; + } + + /** + * Checks if this interface is implemented by clazz. This loads the + * complete hierarchy of clazz on demand and can throw an IOException + * if some classes are not found or broken. If this class is not an + * interface it returns false, but you should check it yourself for + * better performance. + * @param clazz the class to be checked. + * @return true if this is a interface and is implemented by clazz, + * false otherwise + * @exception IOException if hierarchy of clazz could not be loaded. + */ + public boolean implementedBy(ClassInfo clazz) throws IOException { + while (clazz != this && clazz != null) { + if (clazz.status < HIERARCHY) + clazz.load(HIERARCHY); + ClassInfo[] ifaces = clazz.getInterfaces(); + for (int i=0; i< ifaces.length; i++) { + if (implementedBy(ifaces[i])) + return true; + } + clazz = clazz.getSuperclass(); + } + return clazz == this; + } + + public String toString() { + return name; + } +} diff --git a/jode/jode/bytecode/ClassInfo.java.in b/jode/jode/bytecode/ClassInfo.java.in deleted file mode 100644 index abb5a01..0000000 --- a/jode/jode/bytecode/ClassInfo.java.in +++ /dev/null @@ -1,868 +0,0 @@ -/* ClassInfo Copyright (C) 1998-1999 Jochen Hoenicke. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -package jode.bytecode; -import jode.GlobalOptions; -import jode.util.UnifyHash; - -import java.io.DataInputStream; -import java.io.BufferedInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Enumeration; -import @COLLECTIONS@.Iterator; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -/** - * This class does represent a class similar to java.lang.Class. You - * can get the super class and the interfaces.
- * - * The main difference to java.lang.Class is, that the objects are builded - * from a stream containing the .class file, and that it uses the - * Type to represent types instead of Class itself.
- * - *

The InnerClasses attribute

- * - * The InnerClasses attribute is transformed in a special way by this - * class so we want to taker a closer look. According to the inner - * class specification there must be an InnerClass attribute for - * every non top-level class that is referenced somewhere in the - * bytecode. This implies that if this is an inner class, it must - * contain a inner class attribute for itself. Before a class is - * referenced as outer class in an InnerClass attribute, it must be - * described by another InnerClass attribute.
- * - * Since every class references itself, there must be informations - * about the outer class for each class scoped class. If that outer - * class is an outer class again, there must be information about it, - * too. This particular chain of InnerClassInfos is returned by the - * getOuterClasses() method; for convenience in reverse order, i.e. - * current class first, then the outer classes from innermost to - * outermost.
- * - * A valid bytecode must also contain InnerClass infos for each inner - * classes it declares. These information are returned by the - * getInnerClasses() method. The order of these classes is the same - * as in the bytecode attribute. - * - * All remaining attributes are returned by getExtraClasses() in the - * same order as in the bytecode attribute. - * - * @author Jochen Hoenicke - */ -public class ClassInfo extends BinaryInfo { - - private static SearchPath classpath; - - private static final UnifyHash classes = new UnifyHash(); - - private int status = 0; - - private boolean modified = false; - - private int modifiers = -1; - private String name; - private ClassInfo superclass; - private ClassInfo[] interfaces; - private FieldInfo[] fields; - private MethodInfo[] methods; - private InnerClassInfo[] outerClasses; - private InnerClassInfo[] innerClasses; - private InnerClassInfo[] extraClasses; - private String sourceFile; - - public final static ClassInfo javaLangObject = forName("java.lang.Object"); - - public static void setClassPath(String path) { - setClassPath(new SearchPath(path)); - } - - public static void setClassPath(SearchPath path) { - if (classpath != path) { - classpath = path; - Iterator i = classes.iterator(); - while (i.hasNext()) { - ClassInfo ci = (ClassInfo) i.next(); - ci.status = 0; - ci.superclass = null; - ci.fields = null; - ci.interfaces = null; - ci.methods = null; - ci.removeAllAttributes(); - } - } - } - - public static boolean exists(String name) { - return classpath.exists(name.replace('.', '/') + ".class"); - } - - public static boolean isPackage(String name) { - return classpath.isDirectory(name.replace('.', '/')); - } - - public static Enumeration getClassesAndPackages(final String packageName) { - final Enumeration enum = - classpath.listFiles(packageName.replace('.','/')); - return new Enumeration() { - public boolean hasMoreElements() { - return enum.hasMoreElements(); - } - public Object nextElement() { - String name = (String) enum.nextElement(); - if (!name.endsWith(".class")) - // This is a package - return name; - return name.substring(0, name.length()-6); - - } - }; - } - - public static ClassInfo forName(String name) { - if (name == null - || name.indexOf(';') != -1 - || name.indexOf('[') != -1 - || name.indexOf('/') != -1) - throw new IllegalArgumentException("Illegal class name: "+name); - - int hash = name.hashCode(); - Iterator iter = classes.iterateHashCode(hash); - while (iter.hasNext()) { - ClassInfo clazz = (ClassInfo) iter.next(); - if (clazz.name.equals(name)) - return clazz; - } - ClassInfo clazz = new ClassInfo(name); - classes.put(hash, clazz); - return clazz; - } - - private ClassInfo(String name) { - this.name = name; - } - - protected void readAttribute(String name, int length, - ConstantPool cp, - DataInputStream input, - int howMuch) throws IOException { - if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("SourceFile")) { - if (length != 2) - throw new ClassFormatException("SourceFile attribute" - + " has wrong length"); - sourceFile = cp.getUTF8(input.readUnsignedShort()); - } else if ((howMuch & (OUTERCLASSES | INNERCLASSES)) != 0 - && name.equals("InnerClasses")) { - int count = input.readUnsignedShort(); - if (length != 2 + 8 * count) - throw new ClassFormatException - ("InnerClasses attribute has wrong length"); - int innerCount = 0, outerCount = 0, extraCount = 0; - InnerClassInfo[] innerClassInfo = new InnerClassInfo[count]; - for (int i=0; i< count; i++) { - int innerIndex = input.readUnsignedShort(); - int outerIndex = input.readUnsignedShort(); - int nameIndex = input.readUnsignedShort(); - String inner = cp.getClassName(innerIndex); - String outer = - outerIndex != 0 ? cp.getClassName(outerIndex) : null; - String innername = - nameIndex != 0 ? cp.getUTF8(nameIndex) : null; - int access = input.readUnsignedShort(); - if (innername != null && innername.length() == 0) - innername = null; - InnerClassInfo ici = new InnerClassInfo - (inner, outer, innername, access); - - if (outer != null && outer.equals(getName()) - && innername != null) - innerClassInfo[innerCount++] = ici; - else - innerClassInfo[count - (++extraCount)] = ici; - } - /* Now innerClasses are at the front of innerClassInfo array - * in correct order. The other InnerClassInfos are in reverse - * order in the rest of the innerClassInfo array. - */ - - /* We now count the outerClasses. The reverse order is the - * right thing for us. - */ - { - String lastOuterName = getName(); - for (int i = count - extraCount; - i < count && lastOuterName != null; i++) { - InnerClassInfo ici = innerClassInfo[i]; - if (ici.inner.equals(lastOuterName)) { - outerCount++; - extraCount--; - lastOuterName = ici.outer; - } - } - } - if (innerCount > 0) { - innerClasses = new InnerClassInfo[innerCount]; - System.arraycopy(innerClassInfo, 0, - innerClasses, 0, innerCount); - } else - innerClasses = null; - - if (outerCount > 0) { - outerClasses = new InnerClassInfo[outerCount]; - } else - outerClasses = null; - - if (extraCount > 0) { - extraClasses = new InnerClassInfo[extraCount]; - } else - extraClasses = null; - - /* The last part: We split between outer and extra classes. - * In this step we will also revert the order of the extra - * classes. - */ - { - int outerPtr = 0; - String lastOuterName = getName(); - for (int i = count - extraCount - outerCount; - i < count; i++) { - InnerClassInfo ici = innerClassInfo[i]; - - /* If we counted correctly there is no NullPointer - * or ArrayIndexOutOfBoundsException here - */ - if (ici.inner.equals(lastOuterName)) { - outerClasses[outerPtr++] = ici; - lastOuterName = ici.outer; - } else - extraClasses[--extraCount] = ici; - } - } - } else - super.readAttribute(name, length, cp, input, howMuch); - } - - public void read(DataInputStream input, int howMuch) throws IOException { - /* Since we have to read the whole class anyway, we load all - * info, that we may need later and that does not take much memory. - */ - howMuch |= HIERARCHY | INNERCLASSES | OUTERCLASSES; - howMuch &= ~status; - /* header */ - if (input.readInt() != 0xcafebabe) - throw new ClassFormatException("Wrong magic"); - if (input.readUnsignedShort() > 3) - throw new ClassFormatException("Wrong minor"); - if (input.readUnsignedShort() != 45) - throw new ClassFormatException("Wrong major"); - - /* constant pool */ - ConstantPool cpool = new ConstantPool(); - cpool.read(input); - - /* always read modifiers, name, super, ifaces */ - { - modifiers = input.readUnsignedShort(); - String className = cpool.getClassName(input.readUnsignedShort()); - if (!name.equals(className)) - throw new ClassFormatException("wrong name " + className); - String superName = cpool.getClassName(input.readUnsignedShort()); - superclass = superName != null ? ClassInfo.forName(superName) : null; - int count = input.readUnsignedShort(); - interfaces = new ClassInfo[count]; - for (int i=0; i< count; i++) { - interfaces[i] = ClassInfo.forName - (cpool.getClassName(input.readUnsignedShort())); - } - status |= HIERARCHY; - } - - /* fields */ - if ((howMuch & (FIELDS | KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) { - int count = input.readUnsignedShort(); - if ((status & FIELDS) == 0) - fields = new FieldInfo[count]; - for (int i=0; i< count; i++) { - if ((status & FIELDS) == 0) - fields[i] = new FieldInfo(this); - fields[i].read(cpool, input, howMuch); - } - } else { - byte[] skipBuf = new byte[6]; - int count = input.readUnsignedShort(); - for (int i=0; i< count; i++) { - input.readFully(skipBuf); // modifier, name, type - skipAttributes(input); - } - } - - /* methods */ - if ((howMuch & (METHODS | KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) { - int count = input.readUnsignedShort(); - if ((status & METHODS) == 0) - methods = new MethodInfo[count]; - for (int i=0; i< count; i++) { - if ((status & METHODS) == 0) - methods[i] = new MethodInfo(this); - methods[i].read(cpool, input, howMuch); - } - } else { - byte[] skipBuf = new byte[6]; - int count = input.readUnsignedShort(); - for (int i=0; i< count; i++) { - input.readFully(skipBuf); // modifier, name, type - skipAttributes(input); - } - } - - /* attributes */ - readAttributes(cpool, input, howMuch); - status |= howMuch; - } - - public void reserveSmallConstants(GrowableConstantPool gcp) { - for (int i=0; i < fields.length; i++) - fields[i].reserveSmallConstants(gcp); - - for (int i=0; i < methods.length; i++) - methods[i].reserveSmallConstants(gcp); - } - - public void prepareWriting(GrowableConstantPool gcp) { - gcp.putClassName(name); - gcp.putClassName(superclass.getName()); - for (int i=0; i < interfaces.length; i++) - gcp.putClassName(interfaces[i].getName()); - - for (int i=0; i < fields.length; i++) - fields[i].prepareWriting(gcp); - - for (int i=0; i < methods.length; i++) - methods[i].prepareWriting(gcp); - - if (sourceFile != null) { - gcp.putUTF8("SourceFile"); - gcp.putUTF8(sourceFile); - } - if (outerClasses != null || innerClasses != null - || extraClasses != null) { - gcp.putUTF8("InnerClasses"); - int outerCount = outerClasses != null ? outerClasses.length : 0; - for (int i=outerCount; i-- > 0;) { - gcp.putClassName(outerClasses[i].inner); - if (outerClasses[i].outer != null) - gcp.putClassName(outerClasses[i].outer); - if (outerClasses[i].name != null) - gcp.putUTF8(outerClasses[i].name); - } - int innerCount = innerClasses != null ? innerClasses.length : 0; - for (int i=0; i< innerCount; i++) { - gcp.putClassName(innerClasses[i].inner); - if (innerClasses[i].outer != null) - gcp.putClassName(innerClasses[i].outer); - if (innerClasses[i].name != null) - gcp.putUTF8(innerClasses[i].name); - } - int extraCount = extraClasses != null ? extraClasses.length : 0; - for (int i=0; i< extraCount; i++) { - gcp.putClassName(extraClasses[i].inner); - if (extraClasses[i].outer != null) - gcp.putClassName(extraClasses[i].outer); - if (extraClasses[i].name != null) - gcp.putUTF8(extraClasses[i].name); - } - } - prepareAttributes(gcp); - } - - protected int getKnownAttributeCount() { - int count = 0; - if (sourceFile != null) - count++; - if (innerClasses != null || outerClasses != null - || extraClasses != null) - count++; - return count; - } - - public void writeKnownAttributes(GrowableConstantPool gcp, - DataOutputStream output) - throws IOException { - if (sourceFile != null) { - output.writeShort(gcp.putUTF8("SourceFile")); - output.writeInt(2); - output.writeShort(gcp.putUTF8(sourceFile)); - } - if (outerClasses != null || innerClasses != null - || extraClasses != null) { - output.writeShort(gcp.putUTF8("InnerClasses")); - int outerCount = (outerClasses != null) ? outerClasses.length : 0; - int innerCount = (innerClasses != null) ? innerClasses.length : 0; - int extraCount = (extraClasses != null) ? extraClasses.length : 0; - int count = outerCount + innerCount + extraCount; - output.writeInt(2 + count * 8); - output.writeShort(count); - for (int i=outerCount; i-- > 0; ) { - output.writeShort(gcp.putClassName(outerClasses[i].inner)); - output.writeShort(outerClasses[i].outer != null ? - gcp.putClassName(outerClasses[i].outer) : 0); - output.writeShort(outerClasses[i].name != null ? - gcp.putUTF8(outerClasses[i].name) : 0); - output.writeShort(outerClasses[i].modifiers); - } - for (int i=0; i< innerCount; i++) { - output.writeShort(gcp.putClassName(innerClasses[i].inner)); - output.writeShort(innerClasses[i].outer != null ? - gcp.putClassName(innerClasses[i].outer) : 0); - output.writeShort(innerClasses[i].name != null ? - gcp.putUTF8(innerClasses[i].name) : 0); - output.writeShort(innerClasses[i].modifiers); - } - for (int i=0; i< extraCount; i++) { - output.writeShort(gcp.putClassName(extraClasses[i].inner)); - output.writeShort(extraClasses[i].outer != null ? - gcp.putClassName(extraClasses[i].outer) : 0); - output.writeShort(extraClasses[i].name != null ? - gcp.putUTF8(extraClasses[i].name) : 0); - output.writeShort(extraClasses[i].modifiers); - } - } - } - - public void write(DataOutputStream out) throws IOException { - GrowableConstantPool gcp = new GrowableConstantPool(); - reserveSmallConstants(gcp); - prepareWriting(gcp); - - out.writeInt(0xcafebabe); - out.writeShort(3); - out.writeShort(45); - gcp.write(out); - - out.writeShort(modifiers); - out.writeShort(gcp.putClassName(name)); - out.writeShort(gcp.putClassName(superclass.getName())); - out.writeShort(interfaces.length); - for (int i=0; i < interfaces.length; i++) - out.writeShort(gcp.putClassName(interfaces[i].getName())); - - out.writeShort(fields.length); - for (int i=0; i < fields.length; i++) - fields[i].write(gcp, out); - - out.writeShort(methods.length); - for (int i=0; i < methods.length; i++) - methods[i].write(gcp, out); - - writeAttributes(gcp, out); - } - - public void loadInfoReflection(Class clazz, int howMuch) - throws SecurityException { - if ((howMuch & HIERARCHY) != 0) { - modifiers = clazz.getModifiers(); - if (clazz.getSuperclass() == null) - superclass = clazz == Object.class ? null : javaLangObject; - else - superclass = ClassInfo.forName - (clazz.getSuperclass().getName()); - Class[] ifaces = clazz.getInterfaces(); - interfaces = new ClassInfo[ifaces.length]; - for (int i=0; i= 0; ) { - String type = TypeSignature.getSignature(fs[i].getType()); - fields[i] = new FieldInfo - (this, fs[i].getName(), type, fs[i].getModifiers()); - } - } - if ((howMuch & METHODS) != 0 && methods == null) { - Constructor[] cs; - Method[] ms; - try { - cs = clazz.getDeclaredConstructors(); - ms = clazz.getDeclaredMethods(); - } catch (SecurityException ex) { - cs = clazz.getConstructors(); - ms = clazz.getMethods(); - GlobalOptions.err.println - ("Could only get public methods of class " - + name + "."); - } - methods = new MethodInfo[cs.length + ms.length]; - for (int i = cs.length; --i >= 0; ) { - String type = TypeSignature.getSignature - (cs[i].getParameterTypes(), void.class); - methods[i] = new MethodInfo - (this, "", type, cs[i].getModifiers()); - } - for (int i = ms.length; --i >= 0; ) { - String type = TypeSignature.getSignature - (ms[i].getParameterTypes(), ms[i].getReturnType()); - methods[cs.length+i] = new MethodInfo - (this, ms[i].getName(), type, ms[i].getModifiers()); - } - } - if ((howMuch & INNERCLASSES) != 0 && innerClasses == null) { - Class[] is; - try { - is = clazz.getDeclaredClasses(); - } catch (SecurityException ex) { - is = clazz.getClasses(); - GlobalOptions.err.println - ("Could only get public inner classes of class " - + name + "."); - } - if (is.length > 0) { - innerClasses = new InnerClassInfo[is.length]; - for (int i = is.length; --i >= 0; ) { - String inner = is[i].getName(); - int dollar = inner.lastIndexOf('$'); - String name = inner.substring(dollar+1); - innerClasses[i] = new InnerClassInfo - (inner, getName(), name, is[i].getModifiers()); - } - } - } - if ((howMuch & OUTERCLASSES) != 0 && outerClasses == null) { - int count = 0; - Class declarer = clazz.getDeclaringClass(); - while (declarer != null) { - count++; - declarer = declarer.getDeclaringClass(); - } - if (count > 0) { - outerClasses = new InnerClassInfo[count]; - Class current = clazz; - for (int i = 0; i < count; i++) { - declarer = current.getDeclaringClass(); - String name = current.getName(); - int dollar = name.lastIndexOf('$'); - outerClasses[i] = new InnerClassInfo - (name, declarer.getName(), - name.substring(dollar+1), current.getModifiers()); - current = declarer; - } - } - } - status |= howMuch; - } - - public void loadInfo(int howMuch) { - if ((status & howMuch) == howMuch) - return; - if (modified) { - System.err.println("Allocating info 0x" - + Integer.toHexString(howMuch) - + " (status 0x" + Integer.toHexString(status) - + ") in class " + this); - Thread.dumpStack(); - return; - } - try { - DataInputStream input = new DataInputStream - (new BufferedInputStream - (classpath.getFile(name.replace('.', '/') + ".class"))); - read(input, howMuch); - - } catch (IOException ex) { - String message = ex.getMessage(); - if ((howMuch & ~(FIELDS|METHODS|HIERARCHY - |INNERCLASSES|OUTERCLASSES)) != 0) { - GlobalOptions.err.println - ("Can't read class " + name + "."); - ex.printStackTrace(GlobalOptions.err); - throw new NoClassDefFoundError(name); - } - // Try getting the info through the reflection interface - // instead. - Class clazz = null; - try { - clazz = Class.forName(name); - } catch (ClassNotFoundException ex2) { - } catch (NoClassDefFoundError ex2) { - } - try { - if (clazz != null) { - loadInfoReflection(clazz, howMuch); - return; - } - } catch (SecurityException ex2) { - GlobalOptions.err.println - (ex2+" while collecting info about class " + name + "."); - } - - // Give a warning and ``guess'' the hierarchie, methods etc. - GlobalOptions.err.println - ("Can't read class " + name + ", types may be incorrect. (" - + ex.getClass().getName() - + (message != null ? ": " + message : "") + ")"); - ex.printStackTrace(GlobalOptions.err); - - if ((howMuch & HIERARCHY) != 0) { - modifiers = Modifier.PUBLIC; - if (name.equals("java.lang.Object")) - superclass = null; - else - superclass = javaLangObject; - interfaces = new ClassInfo[0]; - } - if ((howMuch & METHODS) != 0) - methods = new MethodInfo[0]; - if ((howMuch & FIELDS) != 0) - fields = new FieldInfo[0]; - status |= howMuch; - } - } - - /** - * This is the counter part to loadInfo. It will drop all info specified - * in howMuch and clean up the memory. - * @param howMuch tells how much info we should drop - */ - public void dropInfo(int howMuch) { - if ((status & howMuch) == 0) - return; - if (modified) { - System.err.println("Dropping info 0x" - + Integer.toHexString(howMuch) - + " (status 0x" + Integer.toHexString(status) - + ") in class " + this); - Thread.dumpStack(); - return; - } - howMuch &= status; - - if ((howMuch & FIELDS) != 0) { - fields = null; - } else if ((status & FIELDS) != 0 - && (howMuch & (KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) { - for (int i=0; i < fields.length; i++) - fields[i].dropInfo(howMuch); - } - - if ((howMuch & METHODS) != 0) { - methods = null; - } else if ((status & METHODS) != 0 - && (howMuch & (KNOWNATTRIBS | UNKNOWNATTRIBS)) != 0) { - for (int i=0; i < methods.length; i++) - methods[i].dropInfo(howMuch); - } - if ((howMuch & KNOWNATTRIBS) != 0) - sourceFile = null; - if ((howMuch & OUTERCLASSES) != 0) - outerClasses = null; - if ((howMuch & INNERCLASSES) != 0) { - innerClasses = null; - extraClasses = null; - } - super.dropInfo(howMuch); - status &= ~howMuch; - } - - public String getName() { - return name; - } - - public String getJavaName() { - /* Don't load attributes for class names not containing a - * dollar sign. - */ - if (name.indexOf('$') == -1) - return getName(); - if (getOuterClasses() != null) { - int last = outerClasses.length-1; - StringBuffer sb = - new StringBuffer(outerClasses[last].outer != null - ? outerClasses[last].outer : "METHOD"); - for (int i=last; i >= 0; i--) - sb.append(".").append(outerClasses[i].name != null - ? outerClasses[i].name : "ANONYMOUS"); - return sb.toString(); - } - return getName(); - } - - public ClassInfo getSuperclass() { - if ((status & HIERARCHY) == 0) - loadInfo(HIERARCHY); - return superclass; - } - - public ClassInfo[] getInterfaces() { - if ((status & HIERARCHY) == 0) - loadInfo(HIERARCHY); - return interfaces; - } - - public int getModifiers() { - if ((status & HIERARCHY) == 0) - loadInfo(HIERARCHY); - return modifiers; - } - - public boolean isInterface() { - return Modifier.isInterface(getModifiers()); - } - - public FieldInfo findField(String name, String typeSig) { - if ((status & FIELDS) == 0) - loadInfo(FIELDS); - for (int i=0; i< fields.length; i++) - if (fields[i].getName().equals(name) - && fields[i].getType().equals(typeSig)) - return fields[i]; - return null; - } - - public MethodInfo findMethod(String name, String typeSig) { - if ((status & METHODS) == 0) - loadInfo(METHODS); - for (int i=0; i< methods.length; i++) - if (methods[i].getName().equals(name) - && methods[i].getType().equals(typeSig)) - return methods[i]; - return null; - } - - public MethodInfo[] getMethods() { - if ((status & METHODS) == 0) - loadInfo(METHODS); - return methods; - } - - public FieldInfo[] getFields() { - if ((status & FIELDS) == 0) - loadInfo(FIELDS); - return fields; - } - - public InnerClassInfo[] getOuterClasses() { - if ((status & OUTERCLASSES) == 0) - loadInfo(OUTERCLASSES); - return outerClasses; - } - - public InnerClassInfo[] getInnerClasses() { - if ((status & INNERCLASSES) == 0) - loadInfo(INNERCLASSES); - return innerClasses; - } - - public InnerClassInfo[] getExtraClasses() { - if ((status & INNERCLASSES) == 0) - loadInfo(INNERCLASSES); - return extraClasses; - } - - public String getSourceFile() { - return sourceFile; - } - - public void setName(String newName) { - name = newName; - modified = true; - } - - public void setSuperclass(ClassInfo newSuper) { - superclass = newSuper; - modified = true; - } - - public void setInterfaces(ClassInfo[] newIfaces) { - interfaces = newIfaces; - modified = true; - } - - public void setModifiers(int newModifiers) { - modifiers = newModifiers; - modified = true; - } - - public void setMethods(MethodInfo[] mi) { - methods = mi; - modified = true; - } - - public void setFields(FieldInfo[] fi) { - fields = fi; - modified = true; - } - - public void setOuterClasses(InnerClassInfo[] oc) { - outerClasses = oc; - modified = true; - } - - public void setInnerClasses(InnerClassInfo[] ic) { - innerClasses = ic; - modified = true; - } - - public void setExtraClasses(InnerClassInfo[] ec) { - extraClasses = ec; - modified = true; - } - - public void setSourceFile(String newSource) { - sourceFile = newSource; - modified = true; - } - - public boolean superClassOf(ClassInfo son) { - while (son != this && son != null) { - son = son.getSuperclass(); - } - return son == this; - } - - public boolean implementedBy(ClassInfo clazz) { - while (clazz != this && clazz != null) { - ClassInfo[] ifaces = clazz.getInterfaces(); - for (int i=0; i< ifaces.length; i++) { - if (implementedBy(ifaces[i])) - return true; - } - clazz = clazz.getSuperclass(); - } - return clazz == this; - } - - public String toString() { - return name; - } -} diff --git a/jode/jode/bytecode/ClassPath.java b/jode/jode/bytecode/ClassPath.java new file mode 100644 index 0000000..bf13df5 --- /dev/null +++ b/jode/jode/bytecode/ClassPath.java @@ -0,0 +1,829 @@ +/* ClassPath Copyright (C) 1998-1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +import java.io.ByteArrayInputStream; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +import java.util.Enumeration; +import java.util.NoSuchElementException; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.Vector; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + +///#def COLLECTIONS java.util +import java.util.Iterator; +///#enddef + +import jode.GlobalOptions; +import jode.util.UnifyHash; + +/** + * A path in which class files are searched for. + * + * Class files can be loaded from several different locations, these + * locations can be: + *
    + *
  • A local directory.
  • + *
  • A local jar or zip file
  • + *
  • A URL (unified resource location), pointing to a directory
  • + *
  • A URL pointing to a jar or zip file.
  • + *
  • A Jar URL (see {@link java.net.JarURLConnection}), useful if + * the jar file is not packed correctly
  • + *
  • The reflection URL reflection:/ This location can + * only load declarations of classes. If a security manager is + * present, it can only load public declarations.
  • + *
+ * + * We use standard java means to find a class file: package correspong + * to directories and the class file must have the .class + * extension. For example if the class path points to + * /home/java, the class java.lang.Object is + * loaded from /home/java/java/lang/Object.class. + * + * A class path can have another ClassPath as fallback. If + * ClassInfo.loadInfo is called and the class isn't found the fallback + * ClassPath is searched instead. This repeats until there is no + * further fallback. + * + * The main method for creating classes is {@link #getClassInfo}. The + * other available methods are useful to find other files in the + * ClassPath and to get a listing of all available files and classes. + * + * A ClassPath handles some IOExceptions and + * SecurityExceptions skipping the path that produced + * them. + * + * @author Jochen Hoenicke + * @version 1.1 */ +public class ClassPath { + + /** + * We need a different pathSeparatorChar, since ':' (used for most + * UNIX System) is used a protocol separator in URLs. + * + * We currently allow both pathSeparatorChar and + * altPathSeparatorChar and decide if it is a protocol separator + * by context. This doesn't always work, so use + * altPathSeparator, or the ClassPath(String[]) + * constructor. + */ + public static final char altPathSeparatorChar = ','; + + private class Path { + public boolean exists(String file) { + return false; + } + public boolean isDirectory(String file) { + return false; + } + public InputStream getFile(String file) throws IOException { + return null; + } + public Enumeration listFiles(String directory) { + return null; + } + + public boolean loadClass(ClassInfo clazz, int howMuch) + throws IOException, ClassFormatException + { + String file = clazz.getName().replace('.', '/') + ".class"; + if (!exists(file)) + return false; + DataInputStream input = new DataInputStream + (new BufferedInputStream + (getFile(file))); + clazz.read(input, howMuch); + return true; + } + } + + private class ReflectionPath extends Path { + public boolean loadClass(ClassInfo classinfo, int howMuch) + throws IOException, ClassFormatException + { + if (howMuch > ClassInfo.DECLARATIONS) + return false; + + Class clazz = null; + try { + clazz = Class.forName(classinfo.getName()); + } catch (ClassNotFoundException ex) { + return false; + } catch (NoClassDefFoundError ex) { + return false; + } + try { + classinfo.loadFromReflection(clazz, howMuch); + return true; + } catch (SecurityException ex) { + return false; + } + } + } + + private class LocalPath extends Path { + private File dir; + + private LocalPath(File path) { + dir = path; + } + + public boolean exists(String filename) { + if (java.io.File.separatorChar != '/') + filename = filename + .replace('/', java.io.File.separatorChar); + try { + return new File(dir, filename).exists(); + } catch (SecurityException ex) { + return false; + } + } + + public boolean isDirectory(String filename) { + if (java.io.File.separatorChar != '/') + filename = filename + .replace('/', java.io.File.separatorChar); + return new File(dir, filename).isDirectory(); + } + + public InputStream getFile(String filename) throws IOException { + if (java.io.File.separatorChar != '/') + filename = filename + .replace('/', java.io.File.separatorChar); + File f = new File(dir, filename); + return new FileInputStream(f); + } + + public Enumeration listFiles(String directory) { + if (File.separatorChar != '/') + directory = directory + .replace('/', File.separatorChar); + File f = new File(dir, directory); + final String[] files = f.list(); + if (files == null) + return null; + + if (!directory.endsWith(File.separator)) + directory += File.separator; + final String prefix = directory; + return new Enumeration() { + int i = 0; + public boolean hasMoreElements() { + return i < files.length; + } + public Object nextElement() { + try { + return files[i++]; + } catch (ArrayIndexOutOfBoundsException ex) { + return new NoSuchElementException(); + } + } + }; + } + } + + private class ZipPath extends Path { + private Hashtable entries = new Hashtable(); + private ZipFile file; + private byte[] contents; + private String prefix; + + private void addEntry(ZipEntry ze) { + String name = ze.getName(); + if (prefix != null) { + if (!name.startsWith(prefix)) + return; + name = name.substring(prefix.length()); + } + + if (ze.isDirectory() + /* || !name.endsWith(".class")*/) + return; + + do { + String dir = ""; + int pathsep = name.lastIndexOf("/"); + if (pathsep != -1) { + dir = name.substring(0, pathsep); + name = name.substring(pathsep+1); + } + + Vector dirContent = (Vector) entries.get(dir); + if (dirContent != null) { + dirContent.addElement(name); + return; + } + + dirContent = new Vector(); + dirContent.addElement(name); + entries.put(dir, dirContent); + name = dir; + } while (name.length() > 0); + } + + private ZipPath(ZipFile zipfile, String prefix) { + this.file = zipfile; + this.prefix = prefix; + + Enumeration zipEnum = file.entries(); + entries = new Hashtable(); + while (zipEnum.hasMoreElements()) { + addEntry((ZipEntry) zipEnum.nextElement()); + } + } + + private ZipPath(byte[] zipcontents, String prefix) + throws IOException + { + this.contents = zipcontents; + this.prefix = prefix; + + // fill entries into hash table + ZipInputStream zis = new ZipInputStream + (new ByteArrayInputStream(zipcontents)); + entries = new Hashtable(); + ZipEntry ze; + while ((ze = zis.getNextEntry()) != null) { + addEntry(ze); + zis.closeEntry(); + } + zis.close(); + } + + public boolean exists(String filename) { + if (entries.containsKey(filename)) + return true; + + String dir = ""; + String name = filename; + int index = filename.lastIndexOf('/'); + if (index >= 0) { + dir = filename.substring(0, index); + name = filename.substring(index+1); + } + Vector directory = (Vector)entries.get(dir); + if (directory != null && directory.contains(name)) + return true; + return false; + } + + public boolean isDirectory(String filename) { + return entries.containsKey(filename); + } + + public InputStream getFile(String filename) throws IOException { + String fullname = prefix != null ? prefix + filename : filename; + if (contents != null) { + ZipInputStream zis = new ZipInputStream + (new ByteArrayInputStream(contents)); + ZipEntry ze; + while ((ze = zis.getNextEntry()) != null) { + if (ze.getName().equals(fullname)) { +///#ifdef JDK11 +/// // The skip method in jdk1.1.7 ZipInputStream +/// // is buggy. We return a wrapper that fixes +/// // this. +/// return new FilterInputStream(zis) { +/// private byte[] tmpbuf = new byte[512]; +/// public long skip(long n) throws IOException { +/// long skipped = 0; +/// while (n > 0) { +/// int count = read(tmpbuf, 0, +/// (int)Math.min(n, 512L)); +/// if (count == -1) +/// return skipped; +/// skipped += count; +/// n -= count; +/// } +/// return skipped; +/// } +/// }; +///#else + return zis; +///#endif + } + zis.closeEntry(); + } + zis.close(); + } else { + ZipEntry ze = file.getEntry(fullname); + if (ze != null) + return file.getInputStream(ze); + } + return null; + } + + public Enumeration listFiles(String directory) { + Vector direntries = (Vector) entries.get(directory); + if (direntries != null) + return direntries.elements(); + return null; + } + } + + private class URLPath extends Path { + URL base; + + private URLPath(URL base) { + this.base = base; + } + + public boolean exists(String filename) { + try { + URL url = new URL(base, filename); + URLConnection conn = url.openConnection(); + conn.connect(); + conn.getInputStream().close(); + return true; + } catch (IOException ex) { + return false; + } + } + + public InputStream getFile(String filename) throws IOException { + try { + URL url = new URL(base, filename); + URLConnection conn = url.openConnection(); + conn.setAllowUserInteraction(true); + return conn.getInputStream(); + } catch (IOException ex) { + return null; + } + } + + public boolean loadClass(ClassInfo clazz, int howMuch) + throws IOException, ClassFormatException + { + String file = clazz.getName().replace('.', '/') + ".class"; + InputStream is = getFile(file); + if (is == null) + return false; + + DataInputStream input = new DataInputStream + (new BufferedInputStream(is)); + clazz.read(input, howMuch); + return true; + } + } + + private Path[] paths; + private UnifyHash classes = new UnifyHash(); + + ClassPath fallback = null; + + /** + * Creates a new class path for the given path. See the class + * description for more information, which kind of paths are + * supported. + * @param path An array of paths. + * @param fallback The fallback classpath. + */ + public ClassPath(String[] paths, ClassPath fallback) { + this.fallback = fallback; + initPath(paths); + } + + /** + * Creates a new class path for the given path. See the class + * description for more information, which kind of paths are + * supported. + * @param path An array of paths. + */ + public ClassPath(String[] paths) { + initPath(paths); + } + + /** + * Creates a new class path for the given path. See the class + * description for more information, which kind of paths are + * supported. + * @param path One or more paths. They should be separated by the + * altPathSeparatorChar or pathSeparatorChar, but the latter is + * deprecated since it may give problems for UNIX machines. + * @see #ClassPath(String[] paths) + */ + public ClassPath(String path, ClassPath fallback) { + this(path); + this.fallback = fallback; + } + + /** + * Creates a new class path for the given path. See the class + * description for more information, which kind of paths are + * supported. + * @param path One or more paths. They should be separated by the + * altPathSeparatorChar or pathSeparatorChar, but the latter is + * deprecated since it may give problems for UNIX machines. + * @see #ClassPath(String[] paths) + */ + public ClassPath(String path) { + // Calculate a good approximation (rounded upwards) of the tokens + // in this path. + int length = 1; + for (int index=path.indexOf(File.pathSeparatorChar); + index != -1; length++) + index = path.indexOf(File.pathSeparatorChar, index+1); + if (File.pathSeparatorChar != altPathSeparatorChar) { + for (int index=path.indexOf(altPathSeparatorChar); + index != -1; length++) + index = path.indexOf(altPathSeparatorChar, index+1); + } + + + String[] tokens = new String[length]; + int i = 0; + for (int ptr=0; ptr < path.length(); ptr++, i++) { + int next = ptr; + while (next < path.length() + && path.charAt(next) != File.pathSeparatorChar + && path.charAt(next) != altPathSeparatorChar) + next++; + + int index = ptr; + colon_separator: + while (next > ptr + && next < path.length() + && path.charAt(next) == ':') { + // Check if this is a URL instead of a pathSeparator + // Since this is a while loop it allows nested urls like + // jar:ftp://ftp.foo.org/pub/foo.jar!/ + + while (index < next) { + char c = path.charAt(index); + // According to RFC 1738 letters, digits, '+', '-' + // and '.' are allowed SCHEMA characters. We + // disallow '.' because it is a good marker that + // the user has specified a filename instead of a + // URL. + if ((c < 'A' || c > 'Z') + && (c < 'a' || c > 'z') + && (c < '0' || c > '9') + && "+-".indexOf(c) == -1) { + break colon_separator; + } + index++; + } + next++; + index++; + while (next < path.length() + && path.charAt(next) != File.pathSeparatorChar + && path.charAt(next) != altPathSeparatorChar) + next++; + } + tokens[i] = path.substring(ptr, next); + ptr = next; + } + initPath(tokens); + } + + private byte[] readURLZip(URLConnection conn) { + int length = conn.getContentLength(); + if (length <= 0) + // Give a approximation if length is unknown + length = 10240; + else + // Increase the length by one, so we hopefully don't need + // to grow the array later (we need a little overshot to + // know when the end is reached). + length++; + + byte[] contents = new byte[length]; + + try { + InputStream is = conn.getInputStream(); + int pos = 0; + for (;;) { + // This is ugly, is.available() may return zero even + // if there are more bytes. + int avail = Math.max(is.available(), 1); + if (pos + is.available() > contents.length) { + // grow the byte array. + byte[] newarr = new byte + [Math.max(2*contents.length, pos + is.available())]; + System.arraycopy(contents, 0, newarr, 0, pos); + contents = newarr; + } + int count = is.read(contents, pos, contents.length-pos); + if (count == -1) + break; + pos += count; + } + if (pos < contents.length) { + // shrink the byte array again. + byte[] newarr = new byte[pos]; + System.arraycopy(contents, 0, newarr, 0, pos); + contents = newarr; + } + return contents; + } catch (IOException ex) { + return null; + } + } + + private void initPath(String[] tokens) { + int length = tokens.length; + paths = new Path[length]; + + for (int i = 0; i < length; i++) { + String path = tokens[i]; + if (path == null) + continue; + + String zipPrefix = null; + // The special reflection URL + if (path.startsWith("reflection:")) { + paths[i] = new ReflectionPath(); + continue; + } + + // We handle jar URL's ourself. + if (path.startsWith("jar:")) { + int index = 0; + do { + index = path.indexOf('!', index); + } while (index != -1 && index != path.length()-1 + && path.charAt(index+1) != '/'); + + if (index == -1 || index == path.length() - 1) { + GlobalOptions.err.println("Warning: Illegal jar url " ++ path + "."); + continue; + } + zipPrefix = path.substring(index+2); + if (!zipPrefix.endsWith("/")) + zipPrefix += "/"; + path = path.substring(4, index); + } + int index = path.indexOf(':'); + if (index != -1 && index < path.length()-2 + && path.charAt(index+1) == '/' + && path.charAt(index+2) == '/') { + // This looks like an URL. + try { + URL base = new URL(path); + try { + URLConnection connection = base.openConnection(); + if (zipPrefix != null + || path.endsWith(".zip") || path.endsWith(".jar") + || connection.getContentType().endsWith("/zip")) { + // This is a zip file. Read it into memory. + byte[] contents = readURLZip(connection); + if (contents != null) + paths[i] = new ZipPath(contents, zipPrefix); + } else + paths[i] = new URLPath(base); + } catch (IOException ex) { + GlobalOptions.err.println + ("Warning: IO exception while accessing " + +path+"."); + } catch (SecurityException ex) { + GlobalOptions.err.println + ("Warning: Security exception while accessing " + +path+"."); + } + } catch (MalformedURLException ex) { + GlobalOptions.err.println + ("Warning: Malformed URL "+ path + "."); + } + } else { + try { + File dir = new File(path); + if (zipPrefix != null || !dir.isDirectory()) { + try { + paths[i] = new ZipPath(new ZipFile(dir), + zipPrefix); + } catch (java.io.IOException ex) { + GlobalOptions.err.println + ("Warning: Can't read "+ path + "."); + } + } else + paths[i] = new LocalPath(dir); + } catch (SecurityException ex) { + GlobalOptions.err.println + ("Warning: SecurityException while accessing " + + path + "."); + } + } + } + } + + + /** + * Creates a new class info for a class residing in this search + * path. This doesn't load the class immediately, this is done by + * ClassInfo.loadInfo. It is no error if class doesn't exists. + * @param classname the dot-separated full qualified name of the class. + * For inner classes you must use the bytecode name with $, + * e.g. java.util.Map$Entry. + * @exception IllegalArgumentException if class name isn't valid. + */ + public ClassInfo getClassInfo(String classname) + { + checkClassName(classname); + int hash = classname.hashCode(); + Iterator iter = classes.iterateHashCode(hash); + while (iter.hasNext()) { + ClassInfo clazz = (ClassInfo) iter.next(); + if (clazz.getName().equals(classname)) + return clazz; + } + ClassInfo clazz = new ClassInfo(classname, this); + classes.put(hash, clazz); + return clazz; + } + + /** + * Checks, if a class with the given name exists somewhere in this + * path. + * @param classname the class name. + * @exception IllegalArgumentException if class name isn't valid. + */ + public boolean existsClass(String classname) { + checkClassName(classname); + return existsFile(classname.replace('.', '/') + ".class"); + } + + /** + * Checks, if a file with the given name exists somewhere in this + * path. + * @param filename the file name. + * @see #existsClass + */ + public boolean existsFile(String filename) { + for (int i=0; i/. + * @return An InputStream for the file. + */ + public InputStream getFile(String filename) throws IOException { + for (int i=0; i < paths.length; i++) { + if (paths[i] != null && paths[i].exists(filename)) + return paths[i].getFile(filename); + } + throw new FileNotFoundException(filename); + } + + /** + * Searches for a filename in the class path and tells if it is a + * directory. + * @param filename the filename. The path components should be separated + * by /. + * @return true, if filename exists and is a directory, false otherwise. + */ + public boolean isDirectory(String filename) { + for (int i=0; i < paths.length; i++) { + if (paths[i] != null && paths[i].exists(filename)) + return paths[i].isDirectory(filename); + } + return false; + } + + /** + * Searches for a filename in the class path and tells if it is a + * package. This is the same as isDirectory. + * @param fqn the full qualified name. The components should be dot + * separated. + * @return true, if filename exists and is a package, false otherwise. + * @see isDirectory + */ + public boolean isPackage(String fqn) { + return isDirectory(fqn.replace('.', '/')); + } + + /** + * Get a list of all files in a given directory. + * @param dirName the directory name. The path components must + * be separated by /. + * @return An enumeration with all files/directories in the given + * directory. If dirName doesn't denote a directory it returns null. + */ + public Enumeration listFiles(final String dirName) { + return new Enumeration() { + int i = 0; + Enumeration enum; + + public boolean hasMoreElements() { + while (true) { + while (enum == null && i < paths.length) { + if (paths[i] != null && paths[i].exists(dirName) + && paths[i].isDirectory(dirName)) + enum = paths[i].listFiles(dirName); + i++; + } + + if (enum == null) + return false; + if (enum.hasMoreElements()) + return true; + enum = null; + } + } + + public Object nextElement() { + if (!hasMoreElements()) + return new NoSuchElementException(); + return enum.nextElement(); + } + }; + } + + /** + * Get a list of all classes and packages in the given package. + * @param package a dot-separated package name. + * @return An enumeration with all class/subpackages in the given + * package. If package doesn't denote a package it returns null. + */ + public Enumeration listClassesAndPackages(String packageName) { + String dir = packageName.replace('.','/'); + final Enumeration enum = listFiles(dir); + final String prefix = dir.length() > 0 ? dir + "/" : dir; + return new Enumeration() { + String next = getNext(); + + private String getNext() { + while (enum.hasMoreElements()) { + String name = (String) enum.nextElement(); + if (name.indexOf('.') == -1 + && isDirectory(prefix + name)) + // This is a package + return name; + if (name.endsWith(".class")) + // This is a class + return name.substring(0, name.length()-6); + } + return null; + } + + public boolean hasMoreElements() { + return next != null; + } + public Object nextElement() { + if (next == null) + throw new NoSuchElementException(); + String result = next; + next = getNext(); + return result; + } + }; + } + + boolean loadClass(ClassInfo clazz, int howMuch) + throws IOException, ClassFormatException + { + for (int i = 0; i < paths.length; i++) { + if (paths[i] != null && paths[i].loadClass(clazz, howMuch)) + return true; + } + if (fallback != null) + return fallback.loadClass(clazz, howMuch); + return false; + } +} diff --git a/jode/jode/bytecode/ConstantInstruction.java b/jode/jode/bytecode/ConstantInstruction.java new file mode 100644 index 0000000..f91d456 --- /dev/null +++ b/jode/jode/bytecode/ConstantInstruction.java @@ -0,0 +1,64 @@ +/* ConstantInstruction Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; +import jode.util.StringQuoter; + +/** + * This class represents an instruction in the byte code. + * + */ +public class ConstantInstruction extends Instruction { + /** + * The typesignature of the class/array. + */ + private Object constant; + + /** + * Standard constructor: creates an opcode with parameter and + * lineNr. + */ + public ConstantInstruction(int opcode, Object constant, int lineNr) { + super(opcode, lineNr); + if (opcode != opc_ldc && opcode != opc_ldc2_w) + throw new IllegalArgumentException("Instruction has no typesig"); + this.constant = constant; + } + + public ConstantInstruction(int opcode, Object constant) { + this(opcode, constant, -1); + } + + public final Object getConstant() + { + return constant; + } + + public final void setConstant(Object constant) + { + this.constant = constant; + } + + public String toString() { + return super.toString() + ' ' + + (constant instanceof String + ? StringQuoter.quote((String) constant) : constant); + } +} + diff --git a/jode/jode/bytecode/ConstantPool.java b/jode/jode/bytecode/ConstantPool.java index 4aec04c..9735959 100644 --- a/jode/jode/bytecode/ConstantPool.java +++ b/jode/jode/bytecode/ConstantPool.java @@ -26,7 +26,7 @@ import java.io.IOException; * * @author Jochen Hoenicke */ -public class ConstantPool { +class ConstantPool { public final static int CLASS = 7; public final static int FIELDREF = 9; public final static int METHODREF = 10; diff --git a/jode/jode/bytecode/FieldInfo.java b/jode/jode/bytecode/FieldInfo.java index a98d2fd..e5568cf 100644 --- a/jode/jode/bytecode/FieldInfo.java +++ b/jode/jode/bytecode/FieldInfo.java @@ -22,10 +22,11 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Modifier; +///#def COLLECTIONEXTRA java.lang +import java.lang.Comparable; +///#enddef -public class FieldInfo extends BinaryInfo { - ClassInfo clazzInfo; - +public final class FieldInfo extends BinaryInfo implements Comparable { int modifier; String name; String typeSig; @@ -34,12 +35,10 @@ public class FieldInfo extends BinaryInfo { boolean syntheticFlag; boolean deprecatedFlag; - public FieldInfo(ClassInfo ci) { - this.clazzInfo = ci; + public FieldInfo() { } - public FieldInfo(ClassInfo ci, String name, String typeSig, int modifier) { - this.clazzInfo = ci; + public FieldInfo(String name, String typeSig, int modifier) { this.name = name; this.typeSig = typeSig; this.modifier = modifier; @@ -49,10 +48,11 @@ public class FieldInfo extends BinaryInfo { ConstantPool cp, DataInputStream input, int howMuch) throws IOException { - if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("ConstantValue")) { + if (howMuch >= ClassInfo.DECLARATIONS + && name.equals("ConstantValue")) { if (length != 2) - throw new ClassFormatException("ConstantValue attribute" - + " has wrong length"); + throw new ClassFormatException + ("ConstantValue attribute has wrong length"); int index = input.readUnsignedShort(); constant = cp.getConstant(index); } else if (name.equals("Synthetic")) { @@ -140,10 +140,8 @@ public class FieldInfo extends BinaryInfo { writeAttributes(constantPool, output); } - public void dropInfo(int howMuch) { - if ((howMuch & KNOWNATTRIBS) != 0) - constant = null; - super.dropInfo(howMuch); + public void dropBody() { + super.dropAttributes(); } public String getName() { @@ -167,7 +165,6 @@ public class FieldInfo extends BinaryInfo { } public Object getConstant() { - clazzInfo.loadInfo(KNOWNATTRIBS); return constant; } @@ -195,6 +192,26 @@ public class FieldInfo extends BinaryInfo { constant = newConstant; } + /** + * Compares two FieldInfo objects for field order. The field + * order is as follows: First the static class intializer followed + * by constructor with type signature sorted lexicographic. Then + * all other fields sorted lexicographically by name. If two + * fields have the same name, they are sorted by type signature, + * though that can only happen for obfuscated code. + * + * @return a positive number if this field follows the other in + * field order, a negative number if it preceeds the + * other, and 0 if they are equal. + * @exception ClassCastException if other is not a ClassInfo. */ + public int compareTo(Object other) { + FieldInfo fi = (FieldInfo) other; + int result = name.compareTo(fi.name); + if (result == 0) + result = typeSig.compareTo(fi.typeSig); + return result; + } + public String toString() { return "Field "+Modifier.toString(modifier)+" "+ typeSig+" "+name; diff --git a/jode/jode/bytecode/GrowableConstantPool.java b/jode/jode/bytecode/GrowableConstantPool.java index ed05930..6319cfa 100644 --- a/jode/jode/bytecode/GrowableConstantPool.java +++ b/jode/jode/bytecode/GrowableConstantPool.java @@ -27,7 +27,7 @@ import java.util.Hashtable; * * @author Jochen Hoenicke */ -public class GrowableConstantPool extends ConstantPool { +class GrowableConstantPool extends ConstantPool { Hashtable entryToIndex = new Hashtable(); boolean written; @@ -243,6 +243,8 @@ public class GrowableConstantPool extends ConstantPool { public void write(DataOutputStream stream) throws IOException { written = true; + if (count > 65536) + throw new ClassFormatError("Too many constants"); stream.writeShort(count); for (int i=1; i< count; i++) { int tag = tags[i]; diff --git a/jode/jode/bytecode/Handler.java b/jode/jode/bytecode/Handler.java index 7bfc8c8..9fa38b1 100644 --- a/jode/jode/bytecode/Handler.java +++ b/jode/jode/bytecode/Handler.java @@ -20,10 +20,75 @@ package jode.bytecode; /** - * A simple class containing the info about an exception handler + * A simple class containing the info about one try-catch block. + * + * @author Jochen Hoenicke */ public class Handler { - public Instruction start, end, catcher; - public String type; + Block start, end, catcher; + String type; + + /** + * The empty handler array. Since handlers are often empty, we don't + * want to create a new object each time. + */ + final static Handler[] EMPTY = new Handler[0]; + + /** + * Creates a new handler. + */ + Handler(Block s, Block e, Block c, String t) { + start = s; + end = e; + catcher = c; + type = t; + } + + /** + * Gets the first basic block of the try. + */ + public Block getStart() { + return start; + } + + /** + * Gets the last basic block of the try. + */ + public Block getEnd() { + return end; + } + + /** + * Gets the first basic block of the exception handler. + */ + public Block getCatcher() { + return catcher; + } + + /** + * Gets the type signature of the exception. + */ + public String getType() { + return type; + } + + public void setStart(Block start) { + this.start = start; + } + + public void setEnd(Block end) { + this.end = end; + } + + public void setCatcher(Block catcher) { + this.catcher = catcher; + } + + /** + * Sets the type signature of the exception. + */ + public void setType(String type) { + this.type = type; + } } diff --git a/jode/jode/bytecode/IncInstruction.java b/jode/jode/bytecode/IncInstruction.java new file mode 100644 index 0000000..ceb8fe7 --- /dev/null +++ b/jode/jode/bytecode/IncInstruction.java @@ -0,0 +1,70 @@ +/* IncInstruction Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +/** + * This class represents an instruction in the byte code. + * + */ +public class IncInstruction extends SlotInstruction { + /** + * The amount of increment. + */ + private int increment; + + /** + * Standard constructor: creates an opcode with parameter and + * lineNr. + */ + public IncInstruction(int opcode, int slot, int increment, int lineNr) { + super(opcode, slot, lineNr); + if (opcode != opc_iinc) + throw new IllegalArgumentException("Instruction has no increment"); + this.increment = increment; + } + + /** + * Creates a simple opcode, without any parameters. + */ + public IncInstruction(int opcode, int slot, int increment) { + this(opcode, slot, increment, -1); + } + + /** + * Get the increment for an opc_iinc instruction. + */ + public final int getIncrement() + { + return increment; + } + + /** + * Set the increment for an opc_iinc instruction. + */ + public final void setIncrement(int incr) + { + this.increment = incr; + } + + + public String toString() { + return super.toString()+' '+increment; + } +} diff --git a/jode/jode/bytecode/InnerClassInfo.java b/jode/jode/bytecode/InnerClassInfo.java deleted file mode 100644 index a96db2a..0000000 --- a/jode/jode/bytecode/InnerClassInfo.java +++ /dev/null @@ -1,42 +0,0 @@ -/* InnerClassInfo Copyright (C) 1999 Jochen Hoenicke. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -package jode.bytecode; - -/** - * A simple class containing the info about an inner class. - */ -public class InnerClassInfo { - public String inner, outer; - public String name; - public int modifiers; - - public InnerClassInfo(String inner, String outer, String name, int modif) { - this.inner = inner; - this.outer = outer; - this.name = name; - this.modifiers = modif; - } - - public String toString() { - return "InnerClassInfo["+inner+","+outer+","+name+"," - +java.lang.reflect.Modifier.toString(modifiers)+"]"; - } -} - diff --git a/jode/jode/bytecode/Instruction.java b/jode/jode/bytecode/Instruction.java index 48fc1e1..501edbc 100644 --- a/jode/jode/bytecode/Instruction.java +++ b/jode/jode/bytecode/Instruction.java @@ -23,268 +23,215 @@ package jode.bytecode; * This class represents an instruction in the byte code. * */ -public final class Instruction implements Opcodes{ +public class Instruction implements Opcodes{ /** - * The opcode of the instruction. We map some opcodes, e.g. - *
-     * iload_[0-3] -> iload, ldc_w -> ldc, wide iinc -> iinc.
-     * 
+ * The opcode and lineNr of the instruction. + * opcode is (lineAndOpcode & 0xff), while + * lineNr is (lineAndOpcode >> 8). + * If line number is not known or unset, it is -1. */ - // a byte would be enough, but then we would need an unsigned convert. - private int opcode; - /** - * If this opcode uses a local this gives the slot. For multianewarray - * this gives the dimension. - */ - private int shortData; - /** - * The address of this opcode. - */ - private int addr; - /** - * Optional object data for this opcode. There are four different - * usages of this field: - *
- *
opc_ldc / opc_ldc2_w
- *
The constant of type Integer/Long/Float/Double/String.
- *
opc_invokexxx / opc_xxxfield / opc_xxxstatic
- *
The field/method Reference
- *
opc_new / opc_checkcast / opc_instanceof / opc_multianewarray
- *
The typesignature of the class/array
- *
opc_lookupswitch
- *
The array of values of type int[]
- *
- */ - private Object objData; - /** - * The successors of this opcodes, where flow may lead to - * (except that nextByAddr is implicit if !alwaysJump). The - * value null means no successor, if there is one succesor, this - * is of type Instruction, otherwise, this is an array of Instruction. - */ - private Object succs; - /** - * The predecessors of this opcode, orthogonal to the succs array. - * This must be null or a non empty array. - */ - private Instruction[] preds; - /** - * The next instruction in code order. - */ - Instruction nextByAddr; + private int lineAndOpcode; + + +// /** +// * Optional object data for this opcode. There are six different +// * usages of this field: +// *
+// *
opc_ldc / opc_ldc2_w
+// *
The constant of type Integer/Long/Float/Double/String.
+// *
opc_invokexxx / opc_xxxfield / opc_xxxstatic
+// *
The field/method Reference
+// *
opc_new / opc_checkcast / opc_instanceof
+// *
The typesignature of the class/array
+// *
opc_lookupswitch
+// *
The array of values of type int[]
+// *
opc_multianewarray
+// *
A DoubleParam: intValue contains dimension, objValue contains +// * reference
+// *
opc_[aildf]{load,store}
+// *
The LocalVariableInfo
+// *
opc_iinc
+// *
A DoubleParam: intValue contains count, objValue contains +// * local variable info.
+// *
+// */ +// private Object param; + +// /** +// * Create a new Instruction suitable for the given opcode. We map +// * some opcodes, so you must always use the mapped opcode. +// *
+//       * [iflda]load_x           -> [iflda]load
+//       * [iflda]store_x          -> [iflda]store
+//       * [ifa]const_xx, ldc_w    -> ldc
+//       * [dl]const_xx            -> ldc2_w
+//       * wide opcode             -> opcode
+//       * tableswitch             -> lookupswitch
+//       * [a]newarray             -> multianewarray
+//       * 
+// */ +// public static Instruction forOpcode(int opcode) { +// if (opcode == opc_iinc) +// return new IncInstruction(opcode); +// else if (opcode == opc_ret +// || opcode >= opc_iload && opcode <= opc_aload +// || opcode >= opc_istore && opcode <= opc_astore) +// return new SlotInstruction(opcode); +// else if (opcode >= opc_getstatic && opcode <= opc_invokeinterface) +// return new ReferenceInstruction(opcode); +// else switch (opcode) { +// case opc_new: +// case opc_checkcast: +// case opc_instanceof: +// return new TypeInstruction(opcode); +// case opc_multianewarray: +// return new TypeDimensionInstruction(opcode); +// default: +// return new Instruction(opcode); +// } +// } + /** - * The previous instruction in code order, useful when changing - * the order. + * Standard constructor: creates an opcode with parameter and + * lineNr. */ - Instruction prevByAddr; + public Instruction(int opcode, int lineNr) { + if (stackDelta.charAt(opcode) == '\177') + throw new IllegalArgumentException("Unknown opcode: "+opcode); + this.lineAndOpcode = (lineNr << 8) | opcode; + } /** - * You can use this field to add some info to each instruction. - * After using, you must set it to null again. - * @XXX Do we really need this. Every field here can quickly take - * half a megabyte! + * Creates a simple opcode, without any parameters. */ - private Object tmpInfo; - public Instruction(int opcode) { - this.opcode = opcode; + this(opcode, -1); } /** - * Returns the opcode of the instruction. We map some opcodes: - *
-     * [iflda]load_x           -> [iflda]load
-     * [iflda]store_x          -> [iflda]store
-     * [ifa]const_xx, ldc_w    -> ldc
-     * [dl]const_xx            -> ldc2_w
-     * wide opcode             -> opcode
-     * tableswitch             -> lookupswitch
-     * [a]newarray             -> multianewarray
-     * 
+ * Returns the opcode of the instruction. */ public final int getOpcode() { - return opcode; + return lineAndOpcode & 0xff; } - /** - * Returns the address of this opcode. As long as you don't remove - * or insert instructions, you can be sure, that the addresses of the - * opcodes are unique, and that - *
-     * instr.getAddr() + instr.getLength() == instr.getNextByAddr().getAddr()
-     * 
-     *
-     * If you insert/remove Instructions, you should be aware that the
-     * above property is not guaranteed anymore.
-     */
-    public final int getAddr() {
-	return addr;
+    public final boolean hasLineNr() {
+	return lineAndOpcode >= 0;
     }
 
-    public final int getNextAddr() {
-	return nextByAddr.addr;
+    public final int getLineNr() {
+	return lineAndOpcode >> 8;
     }
 
-    /**
-     * Returns the length of this opcode.  See getAddr() for some
-     * notes.  Note that the length doesn't necessarily reflect the
-     * real length, when this bytecode is written again, since the
-     * length of an ldc instruction depends on the number of entries
-     * in constant pool, and the order they are allocated.  
-     */
-    public final int getLength() {
-	return getNextAddr() - addr;
+    public final void setLineNr(int nr) {
+	lineAndOpcode = (nr << 8) | (lineAndOpcode & 0xff);
     }
 
-    final void setAddr(int addr) {
-	this.addr = addr;
+    public boolean isStore() {
+	return false;
     }
 
-    public final boolean hasLocalSlot() {
-	return opcode == opc_iinc || opcode == opc_ret
-	    || opcode >= opc_iload && opcode <= opc_aload
-	    || opcode >= opc_istore && opcode <= opc_astore;
+    public boolean hasLocal() {
+	return false;
     }
 	    
-    public final int getLocalSlot()
-    /*{ require { hasLocalSlot()
-                  :: "Instruction has no slot" } }*/
+    public int getLocalSlot()
+    {
+	throw new IllegalArgumentException();
+	// UnsupportedOperationException would be more appropriate
+    }
+
+    public LocalVariableInfo getLocalInfo()
     {
-	return shortData;
+	throw new IllegalArgumentException();
     }
 
-    public final void setLocalSlot(int slot) 
-    /*{ require { hasLocalSlot()
-                  :: "Instruction has no slot" } }*/
+    public void setLocalInfo(LocalVariableInfo info) 
     {
-	shortData = slot;
+	throw new IllegalArgumentException();
+    }
+
+    public void setLocalSlot(int slot) 
+    {
+	throw new IllegalArgumentException();
     }
 
     /**
-     * Optional integer data for this opcode.  There are various uses
-     * for this:
-     * 
- *
opc_iinc
- *
The value by which the constant is increased/decreased. (short)
- *
opc_multianewarray
- *
The number of dimensions (1..255)
- *
+ * Get the increment for an opc_iinc instruction. */ - public final int getIncrement() - /*{ require { opcode == opc_iinc || opcode == opc_multianewarray - || opcode == opc_tableswitch - :: "Instruction has no int data" } }*/ + public int getIncrement() { - /* shortData already used for local slot */ - return ((Short) objData).shortValue(); + throw new IllegalArgumentException(); } /** - * Optional integer data for this opcode. There are various uses - * for this: - *
- *
opc_iinc
- *
The value by which the constant is increased/decreased. (short)
- *
opc_multianewarray
- *
The number of dimensions (1..255)
- *
+ * Set the increment for an opc_iinc instruction. */ - public final void setIncrement(int incr) - /*{ require { opcode == opc_iinc || opcode == opc_multianewarray - :: "Instruction has no int data" } }*/ + public void setIncrement(int incr) { - /* shortData already used for local slot */ - objData = new Short((short) incr); + throw new IllegalArgumentException(); } /** - * + * Get the dimensions for an opc_anewarray opcode. */ - public final int getDimensions() - /*{ require { opcode == opc_multianewarray - :: "Instruction has no dimensions" } }*/ + public int getDimensions() { - return shortData; + throw new IllegalArgumentException(); } /** - * + * Set the dimensions for an opc_anewarray opcode. */ - public final void setDimensions(int dims) - /*{ require { opcode == opc_multianewarray - :: "Instruction has no dimensions" } }*/ + public void setDimensions(int dims) { - shortData = dims; + throw new IllegalArgumentException(); } - public final Object getConstant() - /*{ require { opcode == opc_ldc || opcode == opc_ldc2_w - :: "Instruction has no constant" } }*/ + public Object getConstant() { - return objData; + throw new IllegalArgumentException(); } - public final void setConstant(Object constant) - /*{ require { opcode == opc_ldc || opcode == opc_ldc2_w - :: "Instruction has no constant" } }*/ + public void setConstant(Object constant) { - objData = constant; + throw new IllegalArgumentException(); } - public final Reference getReference() - /*{ require { opcode >= opc_getstatic && opcode <= opc_invokeinterface - :: "Instruction has no reference" } }*/ + public Reference getReference() { - return (Reference) objData; + throw new IllegalArgumentException(); } - public final void setReference(Reference ref) - /*{ require { opcode >= opc_getstatic && opcode <= opc_invokeinterface - :: "Instruction has no reference" } }*/ + public void setReference(Reference ref) { - objData = ref; + throw new IllegalArgumentException(); } - public final String getClazzType() - /*{ require { opcode == opc_new - || opcode == opc_checkcast - || opcode == opc_instanceof - || opcode == opc_multianewarray - :: "Instruction has no typesig" } }*/ + public String getClazzType() { - return (String) objData; + throw new IllegalArgumentException(); } - public final void setClazzType(String type) - /*{ require { opcode == opc_new - || opcode == opc_checkcast - || opcode == opc_instanceof - || opcode == opc_multianewarray - :: "Instruction has no typesig" } }*/ + public void setClazzType(String type) { - objData = type; + throw new IllegalArgumentException(); } - public final int[] getValues() - /*{ require { opcode == opc_lookupswitch - :: "Instruction has no values" } }*/ + public int[] getValues() { - return (int[]) objData; + throw new IllegalArgumentException(); } - public final void setValues(int[] values) - /*{ require { opcode == opc_lookupswitch - :: "Instruction has no values" } }*/ + public void setValues(int[] values) { - objData = values; + throw new IllegalArgumentException(); } public final boolean doesAlwaysJump() { - switch (opcode) { + switch (getOpcode()) { case opc_ret: case opc_goto: - case opc_jsr: - case opc_tableswitch: case opc_lookupswitch: case opc_ireturn: case opc_lreturn: @@ -299,351 +246,6 @@ public final class Instruction implements Opcodes{ } } - public final Instruction[] getPreds() { - return preds; - } - - /** - * Returns true if this opcode has successors, other than the implicit - * getNextByAddr(). - */ - public boolean hasSuccs() { - return succs != null; - } - - /** - * Returns the successors of this opcodes, where flow may lead to - * (except that nextByAddr is implicit if !alwaysJump). The - * value null means that there is no successor. - */ - public final Instruction[] getSuccs() { - if (succs instanceof Instruction) - return new Instruction[] { (Instruction) succs }; - return (Instruction[]) succs; - } - - /** - * Returns the single successor of this opcodes. This gives the - * target of a goto, jsr, or if opcode. - * @return null if there is no successor, otherwise the successor. - * @exception ClassCastException if this has more than one succ. - */ - public final Instruction getSingleSucc() { - return (Instruction) succs; - } - - public final Instruction getPrevByAddr() { - if (prevByAddr.opcode == opc_impdep1) - return null; - return prevByAddr; - } - - public final Instruction getNextByAddr() { - if (nextByAddr.opcode == opc_impdep1) - return null; - return nextByAddr; - } - - public final Object getTmpInfo() { - return tmpInfo; - } - - public final void setTmpInfo(Object info) { - tmpInfo = info; - } - - - // INTERNAL FUNCTIONS TO KEEP PREDS AND SUCCS CONSISTENT - - final void removeSuccs() { - if (succs == null) - return; - if (succs instanceof Instruction[]) { - Instruction[] ss = (Instruction[]) succs; - for (int i = 0; i < ss.length; i++) - if (ss[i] != null) - ss[i].removePredecessor(this); - } else - ((Instruction) succs).removePredecessor(this); - succs = null; - } - - /** - * @param to may be null - */ - private final void promoteSuccs(Instruction from, Instruction to) { - if (succs == from) - succs = to; - else if (succs instanceof Instruction[]) { - Instruction[] ss = (Instruction[]) succs; - for (int i = 0; i < ss.length; i++) - if (ss[i] == from) - ss[i] = to; - } - } - - /** - * @exception ClassCastException if newSuccs is neither an Instruction - * nor an array of instructions. - */ - public final void setSuccs(Object newSuccs) { - if (succs == newSuccs) - return; - removeSuccs(); - if (newSuccs == null) - return; - if (newSuccs instanceof Instruction[]) { - Instruction[] ns = (Instruction[]) newSuccs; - switch (ns.length) { - case 0: - break; - case 1: - succs = ns[0]; - ns[0].addPredecessor(this); - break; - default: - succs = ns; - for (int i = 0; i < ns.length; i++) - ns[i].addPredecessor(this); - break; - } - } else { - succs = newSuccs; - ((Instruction) newSuccs).addPredecessor(this); - } - } - - void addPredecessor(Instruction pred) { - if (preds == null) { - preds = new Instruction[] { pred }; - return; - } - int predsLength = preds.length; - Instruction[] newPreds = new Instruction[predsLength+1]; - System.arraycopy(preds, 0, newPreds, 0, predsLength); - newPreds[predsLength] = pred; - preds = newPreds; - } - - void removePredecessor(Instruction pred) { - /* Hopefully it doesn't matter if this is slow */ - int predLength = preds.length; - if (predLength == 1) { - if (preds[0] != pred) - throw new jode.AssertError - ("removing not existing predecessor"); - preds = null; - } else { - Instruction[] newPreds = new Instruction[predLength-1]; - int j; - for (j = 0; preds[j] != pred; j++) - newPreds[j] = preds[j]; - System.arraycopy(preds, j+1, newPreds, j, predLength - j - 1); - preds = newPreds; - } - } - - // ADDING, REMOVING AND REPLACING INSTRUCTIONS - - /** - * Replaces the opcode of this instruction. You should only use the - * mapped opcodes: - *
-     * [iflda]load_x           -> [iflda]load
-     * [iflda]store_x          -> [iflda]store
-     * [ifa]const_xx, ldc_w    -> ldc
-     * [dl]const_xx            -> ldc2_w
-     * wide opcode             -> opcode
-     * tableswitch             -> lookupswitch
-     * [a]newarray             -> multianewarray
-     * 
- */ - public final void replaceInstruction(Instruction newInstr, - BytecodeInfo codeinfo) { - /* remove predecessors of successors */ - removeSuccs(); - - newInstr.addr = addr; - nextByAddr.prevByAddr = newInstr; - newInstr.nextByAddr = nextByAddr; - prevByAddr.nextByAddr = newInstr; - newInstr.prevByAddr = prevByAddr; - prevByAddr = null; - nextByAddr = null; - - /* promote the successors of the predecessors to newInstr */ - if (preds != null) { - for (int j=0; j < preds.length; j++) - preds[j].promoteSuccs(this, newInstr); - newInstr.preds = preds; - preds = null; - } - - /* adjust exception handlers */ - Handler[] handlers = codeinfo.getExceptionHandlers(); - for (int i=0; i< handlers.length; i++) { - if (handlers[i].start == this) - handlers[i].start = newInstr; - if (handlers[i].end == this) - handlers[i].end = newInstr; - if (handlers[i].catcher == this) - handlers[i].catcher = newInstr; - } - - /* adjust local variable table and line number table */ - LocalVariableInfo[] lvt = codeinfo.getLocalVariableTable(); - if (lvt != null) { - for (int i=0; i< lvt.length; i++) { - if (lvt[i].start == this) - lvt[i].start = newInstr; - if (lvt[i].end == this) - lvt[i].end = newInstr; - } - } - LineNumber[] lnt = codeinfo.getLineNumberTable(); - if (lnt != null) { - for (int i=0; i< lnt.length; i++) { - if (lnt[i].start == this) - lnt[i].start = newInstr; - } - } - } - - void appendInstruction(Instruction newInstr, BytecodeInfo codeinfo) { - newInstr.addr = nextByAddr.addr; - - newInstr.nextByAddr = nextByAddr; - nextByAddr.prevByAddr = newInstr; - newInstr.prevByAddr = this; - nextByAddr = newInstr; - - /* adjust exception handlers end */ - Handler[] handlers = codeinfo.getExceptionHandlers(); - if (handlers != null) { - for (int i=0; i< handlers.length; i++) { - if (handlers[i].end == this) - handlers[i].end = newInstr; - } - } - } - - /** - * Removes this instruction (as if it would be replaced by a nop). - */ - void removeInstruction(BytecodeInfo codeinfo) { - - /* remove from chained list and adjust addr / length */ - prevByAddr.nextByAddr = nextByAddr; - nextByAddr.prevByAddr = prevByAddr; - - /* remove predecessors of successors */ - removeSuccs(); - - /* promote the predecessors to next instruction */ - if (preds != null) { - for (int j=0; j < preds.length; j++) - preds[j].promoteSuccs(this, nextByAddr); - if (nextByAddr.preds == null) - nextByAddr.preds = preds; - else { - Instruction[] newPreds = new Instruction - [nextByAddr.preds.length + preds.length]; - System.arraycopy(nextByAddr.preds, 0, newPreds, 0, - nextByAddr.preds.length); - System.arraycopy(preds, 0, newPreds, nextByAddr.preds.length, - preds.length); - nextByAddr.preds = newPreds; - } - preds = null; - } - - /* adjust exception handlers */ - Handler[] handlers = codeinfo.getExceptionHandlers(); - for (int i=0; i< handlers.length; i++) { - if (handlers[i].start == this && handlers[i].end == this) { - /* Remove the handler. - * This is very seldom, so we can make it slow */ - Handler[] newHandlers = new Handler[handlers.length - 1]; - System.arraycopy(handlers, 0, newHandlers, 0, i); - System.arraycopy(handlers, i+1, newHandlers, i, - handlers.length - (i+1)); - handlers = newHandlers; - codeinfo.setExceptionHandlers(newHandlers); - i--; - } else { - if (handlers[i].start == this) - handlers[i].start = nextByAddr; - if (handlers[i].end == this) - handlers[i].end = prevByAddr; - if (handlers[i].catcher == this) - handlers[i].catcher = nextByAddr; - } - } - - /* adjust local variable table and line number table */ - LocalVariableInfo[] lvt = codeinfo.getLocalVariableTable(); - if (lvt != null) { - for (int i=0; i< lvt.length; i++) { - if (lvt[i].start == this && lvt[i].end == this) { - /* Remove the local variable info. - * This is very seldom, so we can make it slow - */ - LocalVariableInfo[] newLVT = - new LocalVariableInfo[lvt.length - 1]; - System.arraycopy(lvt, 0, newLVT, 0, i); - System.arraycopy(lvt, i+1, newLVT, i, - newLVT.length - i); - lvt = newLVT; - codeinfo.setLocalVariableTable(newLVT); - i--; - } else { - if (lvt[i].start == this) - lvt[i].start = nextByAddr; - if (lvt[i].end == this) - lvt[i].end = prevByAddr; - } - } - } - LineNumber[] lnt = codeinfo.getLineNumberTable(); - if (lnt != null) { - for (int i=0; i< lnt.length; i++) { - if (lnt[i].start == this) { - if (nextByAddr.opcode == opc_impdep1 - || (i+1 < lnt.length - && lnt[i+1].start == nextByAddr)) { - /* Remove the line number. - * This is very seldom, so we can make it slow */ - LineNumber[] newLNT = - new LineNumber[lnt.length - 1]; - System.arraycopy(lnt, 0, newLNT, 0, i); - System.arraycopy(lnt, i+1, newLNT, i, - newLNT.length - i); - lnt = newLNT; - codeinfo.setLineNumberTable(newLNT); - i--; - } else - lnt[i].start = nextByAddr; - } - } - } - - prevByAddr = null; - nextByAddr = null; - } - - public int compareTo(Instruction instr) { - if (addr != instr.addr) - return addr - instr.addr; - if (this == instr) - return 0; - do { - instr = instr.nextByAddr; - if (instr.addr > addr) - return -1; - } while (instr != this); - return 1; - } - /** * This returns the number of stack entries this instruction * pushes and pops from the stack. The result fills the given @@ -656,175 +258,24 @@ public final class Instruction implements Opcodes{ /*{ require { poppush != null && poppush.length == 2 :: "poppush must be an array of two ints" } } */ { - byte delta = (byte) stackDelta.charAt(opcode); - if (delta < 0x40) { - poppush[0] = delta & 7; - poppush[1] = delta >> 3; - } else { - switch (opcode) { - case opc_invokevirtual: - case opc_invokespecial: - case opc_invokestatic: - case opc_invokeinterface: { - Reference ref = getReference(); - String typeSig = ref.getType(); - poppush[0] = opcode != opc_invokestatic ? 1 : 0; - poppush[0] += TypeSignature.getArgumentSize(typeSig); - poppush[1] = TypeSignature.getReturnSize(typeSig); - break; - } - - case opc_putfield: - case opc_putstatic: { - Reference ref = getReference(); - poppush[1] = 0; - poppush[0] = TypeSignature.getTypeSize(ref.getType()); - if (opcode == opc_putfield) - poppush[0]++; - break; - } - case opc_getstatic: - case opc_getfield: { - Reference ref = getReference(); - poppush[1] = TypeSignature.getTypeSize(ref.getType()); - poppush[0] = opcode == opc_getfield ? 1 : 0; - break; - } - - case opc_multianewarray: { - poppush[1] = 1; - poppush[0] = getDimensions(); - break; - } - default: - throw new jode.AssertError("Unknown Opcode: "+opcode); - } - } - } - - public Instruction findMatchingPop() { - int poppush[] = new int[2]; - getStackPopPush(poppush); - - int count = poppush[1]; - Instruction instr = this; - while (true) { - if (instr.succs != null || instr.doesAlwaysJump()) - return null; - instr = instr.nextByAddr; - if (instr.preds != null) - return null; - - instr.getStackPopPush(poppush); - if (count == poppush[0]) - return instr; - count += poppush[1] - poppush[0]; - } + byte delta = (byte) stackDelta.charAt(getOpcode()); + poppush[0] = delta & 7; + poppush[1] = delta >> 3; } - public Instruction findMatchingPush() { - int count = 0; - Instruction instr = this; - int poppush[] = new int[2]; - while (true) { - if (instr.preds != null) - return null; - instr = instr.prevByAddr; - if (instr == null || instr.succs != null || instr.doesAlwaysJump()) - return null; - - instr.getStackPopPush(poppush); - if (count < poppush[1]) { - return count == 0 ? instr : null; - } - count += poppush[0] - poppush[1]; - } - } - - public String getDescription() { - StringBuffer result = new StringBuffer(String.valueOf(addr)) - .append('_').append(Integer.toHexString(hashCode())) - .append(": ").append(opcodeString[opcode]); - if (opcode != opc_lookupswitch) { - if (hasLocalSlot()) - result.append(' ').append(getLocalSlot()); - if (succs != null) - result.append(' ').append(((Instruction) succs).addr); - if (objData != null) - result.append(' ').append(objData); - if (opcode == opc_multianewarray) - result.append(' ').append(getDimensions()); - } else { - int[] values = getValues(); - Instruction[] succs = getSuccs(); - for (int i=0; i < values.length; i++) { - result.append(' ').append(values[i]).append("->") - .append(((Instruction) succs[i]).addr); - } - result.append(' ').append("default: ") - .append(((Instruction) succs[values.length]).addr); - } - return result.toString(); + public final String getDescription() { + return toString(); } public String toString() { - return "" + addr + "_" + Integer.toHexString(hashCode()); + return opcodeString[getOpcode()]; } - private final static String stackDelta = - "\000\010\010\010\010\010\010\010\010\020\020\010\010\010\020\020\010\010\010\010\020\010\020\010\020\010\010\010\010\010\020\020\020\020\010\010\010\010\020\020\020\020\010\010\010\010\012\022\012\022\012\012\012\012\001\002\001\002\001\001\001\001\001\002\002\002\002\001\001\001\001\002\002\002\002\001\001\001\001\003\004\003\004\003\003\003\003\001\002\021\032\043\042\053\064\022\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\011\022\011\022\012\023\012\023\012\023\012\024\012\024\012\024\000\021\011\021\012\012\022\011\021\021\012\022\012\011\011\011\014\012\012\014\014\001\001\001\001\001\001\002\002\002\002\002\002\002\002\000\010\000\001\001\001\002\001\002\001\000\100\100\100\100\100\100\100\100\177\010\011\011\011\001\011\011\001\001\177\100\001\001\000\010"; - - /* stackDelta contains \100 if stack count of opcode is variable + /** + * stackDelta contains \100 if stack count of opcode is variable * \177 if opcode is illegal, or 8*stack_push + stack_pop otherwise - * The above values are extracted from following list with: - * perl -ne'/"(.*)"/ and print $1' - * - * "\000" // nop - * "\010\010\010\010\010\010\010\010" // aconst_null, iconst_m?[0-5] - * "\020\020\010\010\010\020\020" // [lfd]const_[0-2] - * "\010\010\010\010\020" // sipush bipush ldcx - * "\010\020\010\020\010" // [ilfda]load - * "\010\010\010\010" - * "\020\020\020\020" - * "\010\010\010\010" - * "\020\020\020\020" - * "\010\010\010\010" - * "\012\022\012\022\012\012\012\012" // [ilfdabcs]aload - * "\001\002\001\002\001" // [ilfda]store - * "\001\001\001\001" - * "\002\002\002\002" - * "\001\001\001\001" - * "\002\002\002\002" - * "\001\001\001\001" - * "\003\004\003\004\003\003\003\003" // [ilfdabcs]astore - * "\001\002" // pop - * "\021\032\043\042\053\064" // dup2?(_x[12])? - * "\022" // swap - * "\012\024\012\024" // [ilfd]add - * "\012\024\012\024" // [ilfd]sub - * "\012\024\012\024" // [ilfd]mul - * "\012\024\012\024" // [ilfd]div - * "\012\024\012\024" // [ilfd]rem - * "\011\022\011\022" // [ilfd]neg - * "\012\023\012\023\012\023" // [il]u?sh[lr] - * "\012\024\012\024\012\024" // [il](and|or|xor) - * "\000" // opc_iinc - * "\021\011\021" // i2[lfd] - * "\012\012\022" // l2[ifd] - * "\011\021\021" // f2[ild] - * "\012\022\012" // d2[ilf] - * "\011\011\011" // i2[bcs] - * "\014\012\012\014\014" // [lfd]cmp.? - * "\001\001\001\001\001\001" // if.. - * "\002\002\002\002\002\002" // if_icmp.. - * "\002\002" // if_acmp.. - * "\000\010\000\001\001" // goto,jsr,ret, .*switch - * "\001\002\001\002\001\000" // [ilfda]?return - * "\100\100\100\100" // (get/put)(static|field) - * "\100\100\100\100" // invoke.* - * "\177\010\011\011\011" // 186 - 190 - * "\001\011\011\001\001" // 191 - 195 - * "\177\100\001\001" // 196 - 199 - * "\000\010" // goto_w, jsr_w + * The string is created by scripts/createStackDelta.pl */ + protected final static String stackDelta = + "\000\010\010\010\010\010\010\010\010\020\020\010\010\010\020\020\010\010\010\010\020\010\020\010\020\010\010\010\010\010\020\020\020\020\010\010\010\010\020\020\020\020\010\010\010\010\012\022\012\022\012\012\012\012\001\002\001\002\001\001\001\001\001\002\002\002\002\001\001\001\001\002\002\002\002\001\001\001\001\003\004\003\004\003\003\003\003\001\002\021\032\043\042\053\064\022\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\012\024\011\022\011\022\012\023\012\023\012\023\012\024\012\024\012\024\000\021\011\021\012\012\022\011\021\021\012\022\012\011\011\011\014\012\012\014\014\001\001\001\001\001\001\002\002\002\002\002\002\002\002\000\000\000\001\001\001\002\001\002\001\000\100\100\100\100\100\100\100\100\177\010\011\011\011\001\011\011\001\001\177\100\001\001\000\000"; } diff --git a/jode/jode/bytecode/LineNumber.java b/jode/jode/bytecode/LineNumber.java deleted file mode 100644 index c7986f1..0000000 --- a/jode/jode/bytecode/LineNumber.java +++ /dev/null @@ -1,29 +0,0 @@ -/* LineNumber Copyright (C) 1999 Jochen Hoenicke. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -package jode.bytecode; - -/** - * A simple class containing the info of the LineNumberTable - */ -public class LineNumber { - public Instruction start; - public int linenr; -} - diff --git a/jode/jode/bytecode/LocalVariableInfo.java b/jode/jode/bytecode/LocalVariableInfo.java index da78ed4..4b1cf2e 100644 --- a/jode/jode/bytecode/LocalVariableInfo.java +++ b/jode/jode/bytecode/LocalVariableInfo.java @@ -18,13 +18,84 @@ */ package jode.bytecode; +import jode.util.UnifyHash; +///#def COLLECTIONS java.util +import java.util.Iterator; +///#enddef /** * A simple class containing the info of the LocalVariableTable */ -public class LocalVariableInfo { - public Instruction start, end; - public String name, type; - public int slot; -} +public final class LocalVariableInfo { + private String name, type; + private int slot; + private static LocalVariableInfo anonymous[]; + static { + grow(5); + } + private static final UnifyHash unifier = new UnifyHash(); + + private LocalVariableInfo(int slot) { + this.slot = slot; + } + + private LocalVariableInfo(int slot, String name, String type) { + this.slot = slot; + this.name = name; + this.type = type; + } + + private static void grow(int upper) { + LocalVariableInfo[] newAnon = new LocalVariableInfo[upper]; + int start = 0; + if (anonymous != null) { + start = anonymous.length; + System.arraycopy(anonymous, 0, newAnon, 0, start); + } + anonymous = newAnon; + for (int i=start; i< upper; i++) + anonymous[i] = new LocalVariableInfo(i); + } + + public static LocalVariableInfo getInfo(int slot) { + if (slot >= anonymous.length) + grow(Math.max(slot + 1, anonymous.length * 2)); + return anonymous[slot]; + } + public static LocalVariableInfo getInfo(int slot, String name, String type) { + if (name == null && type == null) + return getInfo(slot); + int hash = slot ^ name.hashCode() ^ type.hashCode(); + Iterator iter = unifier.iterateHashCode(hash); + while (iter.hasNext()) { + LocalVariableInfo lvi = (LocalVariableInfo) iter.next(); + if (lvi.slot == slot + && lvi.name.equals(name) + && lvi.type.equals(type)) + return lvi; + } + LocalVariableInfo lvi = new LocalVariableInfo(slot, name, type); + unifier.put(hash, lvi); + return lvi; + } + + public int getSlot() { + return slot; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public String toString() { + String result = ""+slot; + if (name != null) + result += " ["+name+","+type+"]"; + return result; + } +} diff --git a/jode/jode/bytecode/Makefile.am b/jode/jode/bytecode/Makefile.am index fddc5f8..4f62b13 100644 --- a/jode/jode/bytecode/Makefile.am +++ b/jode/jode/bytecode/Makefile.am @@ -1,32 +1,31 @@ ## Input file for automake to generate the Makefile.in used by configure -JAR = @JAR@ -JAVAC = @JAVAC@ -JAVADEP = $(top_builddir)/javaDependencies.pl -subdir=$(subdir)\ - -dependdir=$(top_builddir) -classpath=$(top_builddir):$(top_srcdir) \ - -depfile=Makefile.dep +JAVADEP = $(PERL) -w -s $(top_srcdir)/scripts/javaDependencies.pl \ + -subdir=$(subdir) -dependdir=$(top_builddir) \ + -classpath=$(top_builddir):$(top_srcdir) -depfile=Makefile.dep CLASSPATH = @CLASSPATH@ CLASSLIB = @CLASSLIB@ SUBSTCP = @SUBSTCP@ FULL_CLASSPATH := $(shell $(SUBSTCP) $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB)) MY_JAVA_FILES = \ + BasicBlocks.java \ + BasicBlockReader.java \ + BasicBlockWriter.java \ BinaryInfo.java \ - BytecodeInfo.java \ + Block.java \ ClassFormatException.java \ ClassInfo.java \ + ClassPath.java \ ConstantPool.java \ FieldInfo.java \ GrowableConstantPool.java \ Handler.java \ - InnerClassInfo.java \ Instruction.java \ - LineNumber.java \ LocalVariableInfo.java \ MethodInfo.java \ Opcodes.java \ Reference.java \ - SearchPath.java \ TypeSignature.java noinst_DATA = $(MY_JAVA_FILES:.java=.class) diff --git a/jode/jode/bytecode/MethodInfo.java b/jode/jode/bytecode/MethodInfo.java index 8c52c5f..f38f078 100644 --- a/jode/jode/bytecode/MethodInfo.java +++ b/jode/jode/bytecode/MethodInfo.java @@ -22,39 +22,35 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Modifier; +///#def COLLECTIONEXTRA java.lang +import java.lang.Comparable; +///#enddef -public class MethodInfo extends BinaryInfo { - - ClassInfo clazzInfo; - +public final class MethodInfo extends BinaryInfo implements Comparable { int modifier; String name; String typeSig; - BytecodeInfo bytecode; + BasicBlocks basicblocks; String[] exceptions; boolean syntheticFlag; boolean deprecatedFlag; - public MethodInfo(ClassInfo ci) { - clazzInfo = ci; + public MethodInfo() { } - public MethodInfo(ClassInfo ci, - String name, String typeSig, int modifier) { - this.clazzInfo = ci; + public MethodInfo(String name, String typeSig, int modifier) { this.name = name; this.typeSig = typeSig; this.modifier = modifier; } - protected void readAttribute(String name, int length, ConstantPool cp, - DataInputStream input, - int howMuch) throws IOException { - if ((howMuch & KNOWNATTRIBS) != 0 && name.equals("Code")) { - bytecode = new BytecodeInfo(this); - bytecode.read(cp, input); - } else if ((howMuch & KNOWNATTRIBS) != 0 + void readAttribute(String name, int length, ConstantPool cp, + DataInputStream input, int howMuch) throws IOException { + if (howMuch >= ClassInfo.ALMOSTALL && name.equals("Code")) { + basicblocks = new BasicBlocks(this); + basicblocks.read(cp, input, howMuch); + } else if (howMuch >= ClassInfo.DECLARATIONS && name.equals("Exceptions")) { int count = input.readUnsignedShort(); exceptions = new String[count]; @@ -77,25 +73,25 @@ public class MethodInfo extends BinaryInfo { super.readAttribute(name, length, cp, input, howMuch); } - public void read(ConstantPool constantPool, - DataInputStream input, int howMuch) throws IOException { + void read(ConstantPool constantPool, + DataInputStream input, int howMuch) throws IOException { modifier = input.readUnsignedShort(); name = constantPool.getUTF8(input.readUnsignedShort()); typeSig = constantPool.getUTF8(input.readUnsignedShort()); readAttributes(constantPool, input, howMuch); } - public void reserveSmallConstants(GrowableConstantPool gcp) { - if (bytecode != null) - bytecode.reserveSmallConstants(gcp); + void reserveSmallConstants(GrowableConstantPool gcp) { + if (basicblocks != null) + basicblocks.reserveSmallConstants(gcp); } - public void prepareWriting(GrowableConstantPool gcp) { + void prepareWriting(GrowableConstantPool gcp) { gcp.putUTF8(name); gcp.putUTF8(typeSig); - if (bytecode != null) { + if (basicblocks != null) { gcp.putUTF8("Code"); - bytecode.prepareWriting(gcp); + basicblocks.prepareWriting(gcp); } if (exceptions != null) { gcp.putUTF8("Exceptions"); @@ -109,9 +105,9 @@ public class MethodInfo extends BinaryInfo { prepareAttributes(gcp); } - protected int getKnownAttributeCount() { + int getKnownAttributeCount() { int count = 0; - if (bytecode != null) + if (basicblocks != null) count++; if (exceptions != null) count++; @@ -122,13 +118,12 @@ public class MethodInfo extends BinaryInfo { return count; } - public void writeKnownAttributes(GrowableConstantPool gcp, - DataOutputStream output) + void writeKnownAttributes(GrowableConstantPool gcp, + DataOutputStream output) throws IOException { - if (bytecode != null) { + if (basicblocks != null) { output.writeShort(gcp.putUTF8("Code")); - output.writeInt(bytecode.getSize()); - bytecode.write(gcp, output); + basicblocks.write(gcp, output); } if (exceptions != null) { int count = exceptions.length; @@ -156,18 +151,9 @@ public class MethodInfo extends BinaryInfo { writeAttributes(constantPool, output); } - public void dropInfo(int howMuch) { - if ((howMuch & KNOWNATTRIBS) != 0) { - bytecode = null; - exceptions = null; - } - if (bytecode != null) - bytecode.dropInfo(howMuch); - super.dropInfo(howMuch); - } - - public ClassInfo getClazzInfo() { - return clazzInfo; + public void dropBody() { + basicblocks = null; + super.dropAttributes(); } public String getName() { @@ -182,6 +168,10 @@ public class MethodInfo extends BinaryInfo { return modifier; } + public boolean isConstructor() { + return name.charAt(0) == '<'; + } + public boolean isStatic() { return Modifier.isStatic(modifier); } @@ -194,8 +184,8 @@ public class MethodInfo extends BinaryInfo { return deprecatedFlag; } - public BytecodeInfo getBytecode() { - return bytecode; + public BasicBlocks getBasicBlocks() { + return basicblocks; } public String[] getExceptions() { @@ -222,18 +212,51 @@ public class MethodInfo extends BinaryInfo { deprecatedFlag = flag; } - public void setBytecode(BytecodeInfo newBytecode) { - clazzInfo.loadInfo(KNOWNATTRIBS); - bytecode = newBytecode; + public void setBasicBlocks(BasicBlocks newBasicblocks) { + basicblocks = newBasicblocks; } public void setExceptions(String[] newExceptions) { - clazzInfo.loadInfo(KNOWNATTRIBS); exceptions = newExceptions; } + /** + * Compares two MethodInfo objects for method order. The method + * order is as follows: First the static class intializer followed + * by constructor with type signature sorted lexicographic. Then + * all other methods sorted lexicographically by name. If two + * methods have the same name, they are sorted by type signature. + * + * @return a positive number if this method follows the other in + * method order, a negative number if it preceeds the + * other, and 0 if they are equal. + * @exception ClassCastException if other is not a ClassInfo. + */ + public int compareTo(Object other) { + MethodInfo mi = (MethodInfo) other; + /* Normally constructors should automatically sort themself to + * the beginning, but if method name starts with a digit, the + * order would be destroyed. + * + * The JVM explicitly forbids methods starting with digits, + * nonetheless some obfuscators break this rule. + * + * But note that comes lexicographically before . + */ + if (name.charAt(0) != mi.name.charAt(0)) { + if (name.charAt(0) == '<') + return -1; + if (mi.name.charAt(0) == '<') + return 1; + } + int result = name.compareTo(mi.name); + if (result == 0) + result = typeSig.compareTo(mi.typeSig); + return result; + } + public String toString() { return "Method "+Modifier.toString(modifier)+" "+ - typeSig + " " + clazzInfo.getName() + "."+ name; + typeSig + " " + name; } } diff --git a/jode/jode/bytecode/Opcodes.java b/jode/jode/bytecode/Opcodes.java index 0234647..4b3cc28 100644 --- a/jode/jode/bytecode/Opcodes.java +++ b/jode/jode/bytecode/Opcodes.java @@ -267,4 +267,14 @@ public interface Opcodes { public final static String newArrayTypes = "ZCFDBSIJ"; + + public final static Object[] constants = { + null, + new Integer(-1), new Integer(0), new Integer(1), + new Integer(2), new Integer(3), new Integer(4), new Integer(5), + new Long(0), new Long(1), + new Float(0), new Float(1), new Float(2), + new Double(0), new Double(1) + }; + } diff --git a/jode/jode/bytecode/Reference.java.in b/jode/jode/bytecode/Reference.java similarity index 88% rename from jode/jode/bytecode/Reference.java.in rename to jode/jode/bytecode/Reference.java index f2cd03a..d22aa54 100644 --- a/jode/jode/bytecode/Reference.java.in +++ b/jode/jode/bytecode/Reference.java @@ -19,7 +19,9 @@ package jode.bytecode; import jode.util.UnifyHash; -import @COLLECTIONS@.Iterator; +///#def COLLECTIONS java.util +import java.util.Iterator; +///#enddef /** * This class represents a field or method reference. @@ -67,6 +69,10 @@ public class Reference { } public String toString() { - return clazz + " " + name + " " + type; + String classStr = clazz; + if (clazz.startsWith("L")) + classStr = clazz.substring(1, clazz.length() - 1) + .replace('/', '.'); + return classStr + "." + name + " " + type; } } diff --git a/jode/jode/bytecode/ReferenceInstruction.java b/jode/jode/bytecode/ReferenceInstruction.java new file mode 100644 index 0000000..cc1ec71 --- /dev/null +++ b/jode/jode/bytecode/ReferenceInstruction.java @@ -0,0 +1,101 @@ +/* ReferenceInstruction Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +/** + * This class represents an instruction that needs a reference, i.e. + * a method invocation or field access instruction. + */ +public final class ReferenceInstruction extends Instruction { + private Reference reference; + + /** + * Standard constructor: creates an opcode with parameter and + * lineNr. + */ + public ReferenceInstruction(int opcode, Reference reference, int lineNr) + { + super(opcode, lineNr); + if (opcode < opc_getstatic || opcode > opc_invokeinterface) + throw new IllegalArgumentException("Instruction has no reference"); + this.reference = reference; + } + + /** + * Creates a simple opcode, without any parameters. + */ + public ReferenceInstruction(int opcode, Reference ref) { + this(opcode, ref, -1); + } + + public final Reference getReference() + { + return reference; + } + + public final void setReference(Reference ref) + { + reference = ref; + } + + /** + * This returns the number of stack entries this instruction + * pushes and pops from the stack. The result fills the given + * array. + * + * @param poppush an array of two ints. The first element will + * get the number of pops, the second the number of pushes. + */ + public void getStackPopPush(int[] poppush) + /*{ require { poppush != null && poppush.length == 2 + :: "poppush must be an array of two ints" } } */ + { + Reference ref = getReference(); + String typeSig = ref.getType(); + int opcode = getOpcode(); + switch (opcode) { + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + poppush[0] = opcode != opc_invokestatic ? 1 : 0; + poppush[0] += TypeSignature.getArgumentSize(typeSig); + poppush[1] = TypeSignature.getReturnSize(typeSig); + break; + + case opc_putfield: + case opc_putstatic: + poppush[1] = 0; + poppush[0] = TypeSignature.getTypeSize(typeSig); + if (opcode == opc_putfield) + poppush[0]++; + break; + + case opc_getstatic: + case opc_getfield: + poppush[1] = TypeSignature.getTypeSize(typeSig); + poppush[0] = opcode == opc_getfield ? 1 : 0; + break; + } + } + public String toString() { + return super.toString()+' '+reference; + } +} diff --git a/jode/jode/bytecode/SearchPath.java b/jode/jode/bytecode/SearchPath.java deleted file mode 100644 index 83b864c..0000000 --- a/jode/jode/bytecode/SearchPath.java +++ /dev/null @@ -1,576 +0,0 @@ -/* SearchPath Copyright (C) 1998-1999 Jochen Hoenicke. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -package jode.bytecode; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.StringTokenizer; -import java.util.Vector; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; -import jode.GlobalOptions; - -/** - * This class represents a path of multiple directories and/or zip files, - * where we can search for file names. - * - * @author Jochen Hoenicke - */ -public class SearchPath { - - /** - * We need a different pathSeparatorChar, since ':' (used for most - * UNIX System) is used a protocol separator in URLs. - * - * We currently allow both pathSeparatorChar and - * altPathSeparatorChar and decide if it is a protocol separator - * by context. - */ - public static final char altPathSeparatorChar = ','; - URL[] bases; - byte[][] urlzips; - File[] dirs; - ZipFile[] zips; - String[] zipDirs; - Hashtable[] zipEntries; - - private static void addEntry(Hashtable entries, String name) { - String dir = ""; - int pathsep = name.lastIndexOf("/"); - if (pathsep != -1) { - dir = name.substring(0, pathsep); - name = name.substring(pathsep+1); - } - - Vector dirContent = (Vector) entries.get(dir); - if (dirContent == null) { - dirContent = new Vector(); - entries.put(dir, dirContent); - if (dir != "") - addEntry(entries, dir); - } - dirContent.addElement(name); - } - - private void fillZipEntries(int nr) { - Enumeration zipEnum = zips[nr].entries(); - zipEntries[nr] = new Hashtable(); - while (zipEnum.hasMoreElements()) { - ZipEntry ze = (ZipEntry) zipEnum.nextElement(); - String name = ze.getName(); -// if (name.charAt(0) == '/') -// name = name.substring(1); - if (zipDirs[nr] != null) { - if (!name.startsWith(zipDirs[nr])) - continue; - name = name.substring(zipDirs[nr].length()); - } - if (!ze.isDirectory() && name.endsWith(".class")) - addEntry(zipEntries[nr], name); - } - } - - private void readURLZip(int nr, URLConnection conn) { - int length = conn.getContentLength(); - if (length <= 0) - // Give a approximation if length is unknown - length = 10240; - else - // Increase the length by one, so we hopefully don't need - // to grow the array later (we need a little overshot to - // know when the end is reached). - length++; - - urlzips[nr] = new byte[length]; - try { - InputStream is = conn.getInputStream(); - int pos = 0; - for (;;) { - // This is ugly, is.available() may return zero even - // if there are more bytes. - int avail = Math.max(is.available(), 1); - if (pos + is.available() > urlzips[nr].length) { - // grow the byte array. - byte[] newarr = new byte - [Math.max(2*urlzips[nr].length, pos + is.available())]; - System.arraycopy(urlzips[nr], 0, newarr, 0, pos); - urlzips[nr] = newarr; - } - int count = is.read(urlzips[nr], pos, urlzips[nr].length-pos); - if (count == -1) - break; - pos += count; - } - if (pos < urlzips[nr].length) { - // shrink the byte array again. - byte[] newarr = new byte[pos]; - System.arraycopy(urlzips[nr], 0, newarr, 0, pos); - urlzips[nr] = newarr; - } - } catch (IOException ex) { - GlobalOptions.err.println("IOException while reading " - +"remote zip file "+bases[nr]); - // disable entry - bases[nr] = null; - urlzips[nr] = null; - return; - } - try { - // fill entries into hash table - ZipInputStream zis = new ZipInputStream - (new ByteArrayInputStream(urlzips[nr])); - zipEntries[nr] = new Hashtable(); - ZipEntry ze; - while ((ze = zis.getNextEntry()) != null) { - String name = ze.getName(); -// if (name.charAt(0) == '/') -// name = name.substring(1); - if (zipDirs[nr] != null) { - if (!name.startsWith(zipDirs[nr])) - continue; - name = name.substring(zipDirs[nr].length()); - } - if (!ze.isDirectory() && name.endsWith(".class")) - addEntry(zipEntries[nr], name); - zis.closeEntry(); - } - zis.close(); - } catch (IOException ex) { - GlobalOptions.err.println("Remote zip file "+bases[nr] - +" is corrupted."); - // disable entry - bases[nr] = null; - urlzips[nr] = null; - zipEntries[nr] = null; - return; - } - } - - /** - * Creates a new search path for the given path. - * @param path The path where we should search for files. They - * should be separated by the system dependent pathSeparator. The - * entries may also be zip or jar files. - */ - public SearchPath(String path) { - // Calculate a good approximation (rounded upwards) of the tokens - // in this path. - int length = 1; - for (int index=path.indexOf(File.pathSeparatorChar); - index != -1; length++) - index = path.indexOf(File.pathSeparatorChar, index+1); - if (File.pathSeparatorChar != altPathSeparatorChar) { - for (int index=path.indexOf(altPathSeparatorChar); - index != -1; length++) - index = path.indexOf(altPathSeparatorChar, index+1); - } - bases = new URL[length]; - urlzips = new byte[length][]; - dirs = new File[length]; - zips = new ZipFile[length]; - zipEntries = new Hashtable[length]; - zipDirs = new String[length]; - int i = 0; - for (int ptr=0; ptr < path.length(); ptr++, i++) { - int next = ptr; - while (next < path.length() - && path.charAt(next) != File.pathSeparatorChar - && path.charAt(next) != altPathSeparatorChar) - next++; - - int index = ptr; - colon_separator: - while (next > ptr - && next < path.length() - && path.charAt(next) == ':') { - // Check if this is a URL instead of a pathSeparator - // Since this is a while loop it allows nested urls like - // jar:ftp://ftp.foo.org/pub/foo.jar!/ - - while (index < next) { - char c = path.charAt(index); - // According to RFC 1738 letters, digits, '+', '-' - // and '.' are allowed SCHEMA characters. We - // disallow '.' because it is a good marker that - // the user has specified a filename instead of a - // URL. - if ((c < 'A' || c > 'Z') - && (c < 'a' || c > 'z') - && (c < '0' || c > '9') - && "+-".indexOf(c) == -1) { - break colon_separator; - } - index++; - } - next++; - index++; - while (next < path.length() - && path.charAt(next) != File.pathSeparatorChar - && path.charAt(next) != altPathSeparatorChar) - next++; - } - String token = path.substring(ptr, next); - ptr = next; - - boolean mustBeJar = false; - // We handle jar URL's ourself. - if (token.startsWith("jar:")) { - index = 0; - do { - index = token.indexOf('!', index); - } while (index != -1 && index != token.length()-1 - && token.charAt(index+1) != '/'); - - if (index == -1 || index == token.length()-1) { - GlobalOptions.err.println("Warning: Illegal jar url " - + token + "."); - continue; - } - zipDirs[i] = token.substring(index+2); - if (!zipDirs[i].endsWith("/")) - zipDirs[i] = zipDirs[i] + "/"; - token = token.substring(4, index); - mustBeJar = true; - } - index = token.indexOf(':'); - if (index != -1 && index < token.length()-2 - && token.charAt(index+1) == '/' - && token.charAt(index+2) == '/') { - // This looks like an URL. - try { - bases[i] = new URL(token); - try { - URLConnection connection = bases[i].openConnection(); - if (mustBeJar - || token.endsWith(".zip") || token.endsWith(".jar") - || connection.getContentType().endsWith("/zip")) { - // This is a zip file. Read it into memory. - readURLZip(i, connection); - } - } catch (IOException ex) { - // ignore - } catch (SecurityException ex) { - GlobalOptions.err.println - ("Warning: Security exception while accessing " - +bases[i]+"."); - } - } catch (MalformedURLException ex) { - /* disable entry */ - bases[i] = null; - dirs[i] = null; - } - } else { - try { - dirs[i] = new File(token); - if (mustBeJar || !dirs[i].isDirectory()) { - try { - zips[i] = new ZipFile(dirs[i]); - } catch (java.io.IOException ex) { - /* disable this entry */ - dirs[i] = null; - } - } - } catch (SecurityException ex) { - /* disable this entry */ - GlobalOptions.err.println - ("Warning: SecurityException while accessing " - + token + "."); - dirs[i] = null; - } - } - } - } - - public boolean exists(String filename) { - for (int i=0; i= 0) { - dir = filename.substring(0, index); - name = filename.substring(index+1); - } - Vector directory = (Vector)zipEntries[i].get(dir); - if (directory != null && directory.contains(name)) - return true; - continue; - } - if (bases[i] != null) { - try { - URL url = new URL(bases[i], filename); - URLConnection conn = url.openConnection(); - conn.connect(); - conn.getInputStream().close(); - return true; - } catch (IOException ex) { - /* ignore */ - } - continue; - } - if (dirs[i] == null) - continue; - if (zips[i] != null) { - String fullname = zipDirs[i] != null - ? zipDirs[i] + filename : filename; - ZipEntry ze = zips[i].getEntry(fullname); - if (ze != null) - return true; - } else { - if (java.io.File.separatorChar != '/') - filename = filename - .replace('/', java.io.File.separatorChar); - try { - File f = new File(dirs[i], filename); - if (f.exists()) - return true; - } catch (SecurityException ex) { - /* ignore and take next element */ - } - } - } - return false; - } - - /** - * Searches for a file in the search path. - * @param filename the filename. The path components should be separated - * by /. - * @return An InputStream for the file. - */ - public InputStream getFile(String filename) throws IOException { - for (int i=0; i 0) { - int count = read(tmpbuf, 0, - (int)Math.min(n, 512L)); - if (count == -1) - return skipped; - skipped += count; - n -= count; - } - return skipped; - } - }; -///#else -/// return zis; -///#endif - } - zis.closeEntry(); - } - continue; - } - if (bases[i] != null) { - try { - URL url = new URL(bases[i], filename); - URLConnection conn = url.openConnection(); - conn.setAllowUserInteraction(true); - return conn.getInputStream(); - } catch (SecurityException ex) { - GlobalOptions.err.println("Warning: SecurityException" - +" while accessing " - +bases[i]+filename); - ex.printStackTrace(GlobalOptions.err); - /* ignore and take next element */ - } catch (FileNotFoundException ex) { - /* ignore and take next element */ - } - continue; - } - if (dirs[i] == null) - continue; - if (zips[i] != null) { - String fullname = zipDirs[i] != null - ? zipDirs[i] + filename : filename; - ZipEntry ze = zips[i].getEntry(fullname); - if (ze != null) - return zips[i].getInputStream(ze); - } else { - if (java.io.File.separatorChar != '/') - filename = filename - .replace('/', java.io.File.separatorChar); - try { - File f = new File(dirs[i], filename); - if (f.exists()) - return new FileInputStream(f); - } catch (SecurityException ex) { - GlobalOptions.err.println("Warning: SecurityException" - +" while accessing " - +dirs[i]+filename); - /* ignore and take next element */ - } - } - } - throw new FileNotFoundException(filename); - } - - /** - * Searches for a filename in the search path and tells if it is a - * directory. - * @param filename the filename. The path components should be separated - * by /. - * @return true, if filename exists and is a directory, false otherwise. - */ - public boolean isDirectory(String filename) { - for (int i=0; i/. - * @return An enumeration with all files/directories in the given - * directory. */ - public Enumeration listFiles(final String dirName) { - return new Enumeration() { - int pathNr; - Enumeration zipEnum; - int fileNr; - String localDirName = - (java.io.File.separatorChar != '/') - ? dirName.replace('/', java.io.File.separatorChar) - : dirName; - File currentDir; - String[] files; - - public String findNextFile() { - while (true) { - if (zipEnum != null) { - while (zipEnum.hasMoreElements()) { - return (String) zipEnum.nextElement(); - } - zipEnum = null; - } - if (files != null) { - while (fileNr < files.length) { - String name = files[fileNr++]; - if (name.endsWith(".class")) { - return name; - } else if (name.indexOf(".") == -1) { - /* ignore directories containing a dot. - * they can't be a package directory. - */ - File f = new File(currentDir, name); - if (f.exists() && f.isDirectory()) - return name; - } - } - files = null; - } - if (pathNr == dirs.length) - return null; - - if (zips[pathNr] != null && zipEntries[pathNr] == null) - fillZipEntries(pathNr); - - if (zipEntries[pathNr] != null) { - Vector entries = - (Vector) zipEntries[pathNr].get(dirName); - if (entries != null) - zipEnum = entries.elements(); - } else if (dirs[pathNr] != null) { - try { - File f = new File(dirs[pathNr], localDirName); - if (f.exists() && f.isDirectory()) { - currentDir = f; - files = f.list(); - } - } catch (SecurityException ex) { - GlobalOptions.err.println("Warning: SecurityException" - +" while accessing " - +dirs[pathNr]+localDirName); - /* ignore and take next element */ - } - } - pathNr++; - } - } - - String nextName; - - public boolean hasMoreElements() { - return (nextName != null - || (nextName = findNextFile()) != null); - } - - public Object nextElement() { - if (nextName == null) - return findNextFile(); - else { - String result = nextName; - nextName = null; - return result; - } - } - }; - } -} diff --git a/jode/jode/bytecode/SlotInstruction.java b/jode/jode/bytecode/SlotInstruction.java new file mode 100644 index 0000000..b232837 --- /dev/null +++ b/jode/jode/bytecode/SlotInstruction.java @@ -0,0 +1,94 @@ +/* SlotInstruction Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +/** + * This class represents an instruction in the byte code. + * + */ +public class SlotInstruction extends Instruction { + private LocalVariableInfo lvi; + + /** + */ + public SlotInstruction(int opcode, LocalVariableInfo lvi, int lineNr) { + super(opcode, lineNr); + if (opcode != opc_iinc && opcode != opc_ret + && (opcode < opc_iload || opcode > opc_aload) + && (opcode < opc_istore || opcode > opc_astore)) + throw new IllegalArgumentException("Instruction has no slot"); + this.lvi = lvi; + } + + /** + */ + public SlotInstruction(int opcode, int slot, int lineNr) { + this(opcode, LocalVariableInfo.getInfo(slot), lineNr); + } + + /** + */ + public SlotInstruction(int opcode, LocalVariableInfo lvi) { + this(opcode, lvi, -1); + } + + /** + */ + public SlotInstruction(int opcode, int slot) { + this(opcode, LocalVariableInfo.getInfo(slot), -1); + } + + public boolean isStore() { + int opcode = getOpcode(); + return opcode >= opc_istore && opcode <= opc_astore; + } + + public boolean hasLocal() { + return true; + } + + public final int getLocalSlot() + { + return lvi.getSlot(); + } + + public final LocalVariableInfo getLocalInfo() + { + return lvi; + } + + public final void setLocalInfo(LocalVariableInfo info) + { + this.lvi = info; + } + + public final void setLocalSlot(int slot) + { + if (lvi.getName() == null) + this.lvi = LocalVariableInfo.getInfo(slot); + else + this.lvi = LocalVariableInfo.getInfo(slot, + lvi.getName(), lvi.getType()); + } + + public String toString() { + return super.toString()+' '+lvi; + } +} diff --git a/jode/jode/bytecode/SwitchInstruction.java b/jode/jode/bytecode/SwitchInstruction.java new file mode 100644 index 0000000..b37440f --- /dev/null +++ b/jode/jode/bytecode/SwitchInstruction.java @@ -0,0 +1,67 @@ +/* SwitchInstruction Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; +import jode.util.StringQuoter; + +/** + * This class represents an instruction in the byte code. + * + */ +public class SwitchInstruction extends Instruction { + /** + * The values for this switch. + */ + private int[] values; + + /** + * Standard constructor: creates an opcode with parameter and + * lineNr. + */ + public SwitchInstruction(int opcode, int[] values, int lineNr) { + super(opcode, lineNr); + if (opcode != opc_lookupswitch) + throw new IllegalArgumentException("Instruction is no switch"); + this.values = values; + } + + /** + * Creates a simple opcode, without any parameters. + */ + public SwitchInstruction(int opcode, int[] values) { + this(opcode, values, -1); + } + + public final int[] getValues() + { + return values; + } + + public final void setValues(int[] values) + { + this.values = values; + } + + public String toString() { + StringBuffer sb = new StringBuffer(opcodeString[getOpcode()]); + for (int i=0; i< values.length; i++) + sb.append(' ').append(values[i]); + return sb.toString(); + } +} diff --git a/jode/jode/bytecode/TypeDimensionInstruction.java b/jode/jode/bytecode/TypeDimensionInstruction.java new file mode 100644 index 0000000..b077f3b --- /dev/null +++ b/jode/jode/bytecode/TypeDimensionInstruction.java @@ -0,0 +1,86 @@ +/* TypeDimensionInstruction Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +/** + * This class represents an instruction in the byte code. + * + */ +public class TypeDimensionInstruction extends TypeInstruction { + /** + * The dimension of this multianewarray operation. + */ + private int dimension; + + /** + * Standard constructor: creates an opcode with parameter and + * lineNr. + */ + public TypeDimensionInstruction(int opcode, String type, int dimension, + int lineNr) { + super(opcode, type, lineNr); + if (opcode != opc_multianewarray) + throw new IllegalArgumentException("Instruction has no dimension"); + this.dimension = dimension; + } + + /** + * Creates a simple opcode, without any parameters. + */ + public TypeDimensionInstruction(int opcode, String type, int dimension) { + this(opcode, type, dimension, -1); + } + + /** + * Get the dimensions for an opc_anewarray opcode. + */ + public final int getDimensions() + { + return dimension; + } + + /** + * Get the dimensions for an opc_anewarray opcode. + */ + public final void setDimensions(int dim) + { + dimension = dim; + } + + /** + * This returns the number of stack entries this instruction + * pushes and pops from the stack. The result fills the given + * array. + * + * @param poppush an array of two ints. The first element will + * get the number of pops, the second the number of pushes. + */ + public void getStackPopPush(int[] poppush) + /*{ require { poppush != null && poppush.length == 2 + :: "poppush must be an array of two ints" } } */ + { + poppush[0] = dimension; + poppush[1] = 1; + } + + public String toString() { + return super.toString()+' '+dimension; + } +} diff --git a/jode/jode/bytecode/TypeInstruction.java b/jode/jode/bytecode/TypeInstruction.java new file mode 100644 index 0000000..abce932 --- /dev/null +++ b/jode/jode/bytecode/TypeInstruction.java @@ -0,0 +1,64 @@ +/* TypeInstruction Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.bytecode; + +/** + * This class represents an instruction in the byte code. + * + */ +public class TypeInstruction extends Instruction { + /** + * The typesignature of the class/array. + */ + private String typeSig; + + /** + * Standard constructor: creates an opcode with parameter and + * lineNr. + */ + public TypeInstruction(int opcode, String typeSig, int lineNr) { + super(opcode, lineNr); + if (opcode != opc_new && opcode != opc_checkcast + && opcode != opc_instanceof && opcode != opc_multianewarray) + throw new IllegalArgumentException("Instruction has no typesig"); + this.typeSig = typeSig; + } + + /** + * Creates a simple opcode, without any parameters. + */ + public TypeInstruction(int opcode, String typeSig) { + this(opcode, typeSig, -1); + } + + public final String getClazzType() + { + return typeSig; + } + + public final void setClazzType(String type) + { + typeSig = type; + } + + public String toString() { + return super.toString()+' '+typeSig; + } +} diff --git a/jode/jode/bytecode/TypeSignature.java b/jode/jode/bytecode/TypeSignature.java index 54113a4..53e9b56 100644 --- a/jode/jode/bytecode/TypeSignature.java +++ b/jode/jode/bytecode/TypeSignature.java @@ -19,6 +19,7 @@ package jode.bytecode; import jode.AssertError; +import jode.util.UnifyHash; /** * This class contains some static methods to handle type signatures. @@ -143,10 +144,10 @@ public class TypeSignature { return typeSig.substring(1); } - public static ClassInfo getClassInfo(String typeSig) { + public static ClassInfo getClassInfo(ClassPath classpath, String typeSig) { if (typeSig.charAt(0) != 'L') throw new IllegalArgumentException(); - return ClassInfo.forName + return classpath.getClassInfo (typeSig.substring(1, typeSig.length()-1).replace('/', '.')); }