From 996fc49dbd6b29864fff11f910df3bc7b3f4719c Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 20 Jul 1999 16:04:32 +0000 Subject: [PATCH] collection interface for instructions. made instructions smaller canonicalized switch opcodes use UnifyHash More methods for TypeSignature git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1095 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/bytecode/BinaryInfo.java.in | 58 ++- jode/jode/bytecode/BytecodeInfo.java.in | 610 ++++++++++++++---------- jode/jode/bytecode/ClassInfo.java.in | 73 +-- jode/jode/bytecode/Instruction.java | 471 +++++++++++------- jode/jode/bytecode/Reference.java | 94 ---- jode/jode/bytecode/Reference.java.in | 72 +++ jode/jode/bytecode/TypeSignature.java | 73 ++- 7 files changed, 861 insertions(+), 590 deletions(-) delete mode 100644 jode/jode/bytecode/Reference.java create mode 100644 jode/jode/bytecode/Reference.java.in diff --git a/jode/jode/bytecode/BinaryInfo.java.in b/jode/jode/bytecode/BinaryInfo.java.in index c4ddb15..612d47e 100644 --- a/jode/jode/bytecode/BinaryInfo.java.in +++ b/jode/jode/bytecode/BinaryInfo.java.in @@ -27,6 +27,7 @@ import java.io.InputStream; import jode.util.SimpleMap; import @COLLECTIONS@.Map; +import @COLLECTIONS@.Collections; import @COLLECTIONS@.Iterator; @@ -44,7 +45,7 @@ public class BinaryInfo { public static final int OUTERCLASSES = 0x40; public static final int FULLINFO = 0xff; - private Map unknownAttributes = new SimpleMap(); + private Map unknownAttributes = null; protected void skipAttributes(DataInputStream input) throws IOException { int count = input.readUnsignedShort(); @@ -70,8 +71,11 @@ public class BinaryInfo { int howMuch) throws IOException { byte[] data = new byte[length]; input.readFully(data); - if ((howMuch & ALL_ATTRIBUTES) != 0) + if ((howMuch & ALL_ATTRIBUTES) != 0) { + if (unknownAttributes == null) + unknownAttributes = new SimpleMap(); unknownAttributes.put(name, data); + } } static class ConstrainedInputStream extends FilterInputStream { @@ -129,7 +133,7 @@ public class BinaryInfo { DataInputStream input, int howMuch) throws IOException { int count = input.readUnsignedShort(); - unknownAttributes.clear(); + unknownAttributes = null; for (int i=0; i< count; i++) { String attrName = constantPool.getUTF8(input.readUnsignedShort()); @@ -144,6 +148,8 @@ public class BinaryInfo { } protected void prepareAttributes(GrowableConstantPool gcp) { + if (unknownAttributes == null) + return; Iterator i = unknownAttributes.keySet().iterator(); while (i.hasNext()) gcp.putUTF8((String) i.next()); @@ -157,45 +163,59 @@ public class BinaryInfo { protected void writeAttributes (GrowableConstantPool constantPool, DataOutputStream output) throws IOException { - int count = unknownAttributes.size() + getKnownAttributeCount(); + int count = getKnownAttributeCount(); + if (unknownAttributes != null) + count += unknownAttributes.size(); output.writeShort(count); writeKnownAttributes(constantPool, output); - Iterator i = unknownAttributes.entrySet().iterator(); - while (i.hasNext()) { - Map.Entry e = (Map.Entry) i.next(); - String name = (String) e.getKey(); - byte[] data = (byte[]) e.getValue(); - output.writeShort(constantPool.putUTF8(name)); - output.writeInt(data.length); - output.write(data); + if (unknownAttributes != null) { + Iterator i = unknownAttributes.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry e = (Map.Entry) i.next(); + String name = (String) e.getKey(); + byte[] data = (byte[]) e.getValue(); + output.writeShort(constantPool.putUTF8(name)); + output.writeInt(data.length); + output.write(data); + } } } public int getAttributeSize() { int size = 2; /* attribute count */ - Iterator i = unknownAttributes.values().iterator(); - while (i.hasNext()) - size += 2 + 4 + ((byte[]) i.next()).length; + if (unknownAttributes != null) { + Iterator i = unknownAttributes.values().iterator(); + while (i.hasNext()) + size += 2 + 4 + ((byte[]) i.next()).length; + } return size; } public byte[] findAttribute(String name) { - return (byte[]) unknownAttributes.get(name); + if (unknownAttributes != null) + return (byte[]) unknownAttributes.get(name); + return null; } public Iterator getAttributes() { - return unknownAttributes.values().iterator(); + if (unknownAttributes != null) + return unknownAttributes.values().iterator(); + return Collections.EMPTY_SET.iterator(); } public void setAttribute(String name, byte[] content) { + if (unknownAttributes == null) + unknownAttributes = new SimpleMap(); unknownAttributes.put(name, content); } public byte[] removeAttribute(String name) { - return (byte[]) unknownAttributes.remove(name); + if (unknownAttributes != null) + return (byte[]) unknownAttributes.remove(name); + return null; } public void removeAllAttributes() { - unknownAttributes.clear(); + unknownAttributes = null; } } diff --git a/jode/jode/bytecode/BytecodeInfo.java.in b/jode/jode/bytecode/BytecodeInfo.java.in index b88699f..f57eb99 100644 --- a/jode/jode/bytecode/BytecodeInfo.java.in +++ b/jode/jode/bytecode/BytecodeInfo.java.in @@ -29,9 +29,10 @@ import java.util.Vector; import java.util.Enumeration; import java.util.NoSuchElementException; -import @COLLECTIONS@.Collection; -import @COLLECTIONS@.AbstractCollection; +import @COLLECTIONS@.List; +import @COLLECTIONS@.AbstractSequentialList; import @COLLECTIONS@.Iterator; +import @COLLECTIONS@.ListIterator; /** @@ -44,16 +45,136 @@ import @COLLECTIONS@.Iterator; */ public class BytecodeInfo extends BinaryInfo implements Opcodes { - MethodInfo methodInfo; + private MethodInfo methodInfo; + private int maxStack, maxLocals; + private Handler[] exceptionHandlers; + private LocalVariableInfo[] lvt; + private LineNumber[] lnt; - ConstantPool cp; - int maxStack, maxLocals; - int codeLength; - Instruction firstInstr = null; - int instructionCount = 0; - Handler[] exceptionHandlers; - LocalVariableInfo[] lvt; - LineNumber[] lnt; + private InstructionList instructions; + + private class InstructionList extends AbstractSequentialList { + Instruction borderInstr; + int instructionCount = 0; + + InstructionList() { + borderInstr = new Instruction(opc_xxxunusedxxx); + borderInstr.nextByAddr = borderInstr.prevByAddr = borderInstr; + } + + public int size() { + return instructionCount; + } + + private 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 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); + 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; @@ -74,7 +195,7 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if ((howMuch & ALL_ATTRIBUTES) != 0 && name.equals("LocalVariableTable")) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0) - GlobalOptions.err.println("LocalVariableTable of "+methodInfo.clazzInfo.getName() + "." + methodInfo.getName()); + GlobalOptions.err.println("LocalVariableTable of "+methodInfo); int count = input.readUnsignedShort(); if (length != 2 + count * 10) { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_LVT) != 0) @@ -89,22 +210,21 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { int nameIndex = input.readUnsignedShort(); int typeIndex = input.readUnsignedShort(); int slot = input.readUnsignedShort(); - Instruction startInstr, endInstr; - for (startInstr = firstInstr; - startInstr.getAddr() < start && startInstr != null; - startInstr = startInstr.getNextByAddr()) { - /* empty */ - } - endInstr = startInstr; - if (startInstr != null) { - while (endInstr.getNextByAddr() != null - && endInstr.getNextByAddr().getAddr() < end) - endInstr = endInstr.getNextByAddr(); + Instruction startInstr = null; + Instruction endInstr = null; + + for (Iterator iter = instructions.iterator(); + iter.hasNext(); ) { + Instruction instr = (Instruction) iter.next(); + if (instr.getAddr() == start) + startInstr = instr; + if (instr.getNextAddr() == end) + endInstr = instr; + if (instr.getAddr() >= end) + break; } if (startInstr == null - || startInstr.getAddr() != start || endInstr == null - || endInstr.getAddr() + endInstr.getLength() != end || nameIndex == 0 || typeIndex == 0 || slot >= maxLocals || cp.getTag(nameIndex) != cp.UTF8 @@ -142,14 +262,15 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { for (int i = 0; i < count; i++) { lnt[i] = new LineNumber(); int start = input.readUnsignedShort(); - Instruction startInstr; - for (startInstr = firstInstr; - startInstr.getAddr() < start && startInstr != null; - startInstr = startInstr.getNextByAddr()) { - /* empty */ + Instruction startInstr = null; + for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { + Instruction instr = (Instruction) iter.next(); + if (instr.getAddr() == start) + startInstr = instr; + if (instr.getAddr() >= start) + break; } - if (startInstr == null - || startInstr.getAddr() != start) { + if (startInstr == null) { GlobalOptions.err.println ("Illegal entry, ignoring LineNumberTable table"); lnt = null; @@ -164,25 +285,17 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { public void read(ConstantPool cp, DataInputStream input) throws IOException { - this.cp = cp; maxStack = input.readUnsignedShort(); maxLocals = input.readUnsignedShort(); - codeLength = input.readInt(); + instructions = new InstructionList(); + int codeLength = input.readInt(); Instruction[] instrs = new Instruction[codeLength]; int[][] succAddrs = new int[codeLength][]; - int[] predcounts = new int[codeLength]; { int addr = 0; - firstInstr = new Instruction(this); - instructionCount++; - Instruction lastInstr = null; while (addr < codeLength) { - Instruction instr = lastInstr != null - ? lastInstr.appendInstruction(opc_nop) : firstInstr; - - instrs[addr] = instr; - lastInstr = instr; - + Instruction instr; + int length; int opcode = input.readUnsignedByte(); if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) @@ -193,7 +306,6 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { switch (opcode) { case opc_wide: { int wideopcode = input.readUnsignedByte(); - instr.replaceInstruction(wideopcode); switch (wideopcode) { case opc_iload: case opc_fload: case opc_aload: case opc_istore: case opc_fstore: case opc_astore: { @@ -201,8 +313,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals) throw new ClassFormatError ("Invalid local slot "+slot); + instr = new Instruction(wideopcode); instr.setLocalSlot(slot); - instr.setLength(4); + length = 4; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print @@ -215,12 +328,13 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals-1) throw new ClassFormatError ("Invalid local slot "+slot); + instr = new Instruction(wideopcode); instr.setLocalSlot(slot); - instr.setLength(4); + length = 4; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+opcodeString[wideopcode] - +" "+slot); + GlobalOptions.err.print + (" " + opcodeString[wideopcode] + " " + slot); break; } case opc_ret: { @@ -228,8 +342,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals) throw new ClassFormatError ("Invalid local slot "+slot); + instr = new Instruction(wideopcode); instr.setLocalSlot(slot); - instr.setLength(4); + length = 4; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" ret "+slot); @@ -240,13 +355,14 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals) throw new ClassFormatError ("Invalid local slot "+slot); + instr = new Instruction(wideopcode); instr.setLocalSlot(slot); - instr.setIntData(input.readShort()); - instr.setLength(6); + instr.setIncrement(input.readShort()); + length = 6; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" iinc "+slot - +" "+instr.getIntData()); + GlobalOptions.err.print + (" iinc " + slot + " " + instr.getIncrement()); break; } default: @@ -269,10 +385,10 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals) throw new ClassFormatError ("Invalid local slot "+slot); - instr.replaceInstruction(opc_iload + - (opcode-opc_iload_0)/4); + instr = new Instruction(opc_iload + + (opcode-opc_iload_0)/4); instr.setLocalSlot(slot); - instr.setLength(1); + length = 1; break; } case opc_istore_0: case opc_istore_1: @@ -285,10 +401,10 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals) throw new ClassFormatError ("Invalid local slot "+slot); - instr.replaceInstruction(opc_istore + + instr = new Instruction(opc_istore + (opcode-opc_istore_0)/4); instr.setLocalSlot(slot); - instr.setLength(1); + length = 1; break; } case opc_lstore_0: case opc_lstore_1: @@ -299,10 +415,10 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals-1) throw new ClassFormatError ("Invalid local slot "+slot); - instr.replaceInstruction(opc_istore + instr = new Instruction(opc_istore + (opcode-opc_istore_0)/4); instr.setLocalSlot(slot); - instr.setLength(1); + length = 1; break; } case opc_iload: case opc_fload: case opc_aload: @@ -311,9 +427,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals) throw new ClassFormatError ("Invalid local slot "+slot); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setLocalSlot(slot); - instr.setLength(2); + length = 2; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+slot); @@ -325,9 +441,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals - 1) throw new ClassFormatError ("Invalid local slot "+slot); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setLocalSlot(slot); - instr.setLength(2); + length = 2; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+slot); @@ -338,9 +454,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals) throw new ClassFormatError ("Invalid local slot "+slot); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setLocalSlot(slot); - instr.setLength(2); + length = 2; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+slot); @@ -351,27 +467,27 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { 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.replaceInstruction(opc_ldc); + instr = new Instruction(opc_ldc); instr.setConstant (constants[opcode - opc_aconst_null]); - instr.setLength(1); + length = 1; break; case opc_lconst_0: case opc_lconst_1: case opc_dconst_0: case opc_dconst_1: - instr.replaceInstruction(opc_ldc2_w); + instr = new Instruction(opc_ldc2_w); instr.setConstant (constants[opcode - opc_aconst_null]); - instr.setLength(1); + length = 1; break; case opc_bipush: - instr.replaceInstruction(opc_ldc); + instr = new Instruction(opc_ldc); instr.setConstant(new Integer(input.readByte())); - instr.setLength(2); + length = 2; break; case opc_sipush: - instr.replaceInstruction(opc_ldc); + instr = new Instruction(opc_ldc); instr.setConstant(new Integer(input.readShort())); - instr.setLength(3); + length = 3; break; case opc_ldc: { int index = input.readUnsignedByte(); @@ -380,9 +496,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { && tag != cp.INTEGER && tag != cp.FLOAT) throw new ClassFormatException ("wrong constant tag: "+tag); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setConstant(cp.getConstant(index)); - instr.setLength(2); + length = 2; break; } case opc_ldc_w: { @@ -392,9 +508,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { && tag != cp.INTEGER && tag != cp.FLOAT) throw new ClassFormatException ("wrong constant tag: "+tag); - instr.replaceInstruction(opc_ldc); + instr = new Instruction(opc_ldc); instr.setConstant(cp.getConstant(index)); - instr.setLength(3); + length = 3; break; } case opc_ldc2_w: { @@ -403,9 +519,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (tag != cp.LONG && tag != cp.DOUBLE) throw new ClassFormatException ("wrong constant tag: "+tag); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setConstant(cp.getConstant(index)); - instr.setLength(3); + length = 3; break; } case opc_iinc: { @@ -413,14 +529,14 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (slot >= maxLocals) throw new ClassFormatError ("Invalid local slot "+slot); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setLocalSlot(slot); - instr.setIntData(input.readByte()); - instr.setLength(3); + instr.setIncrement(input.readByte()); + length = 3; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) - GlobalOptions.err.print(" "+slot - +" "+instr.getIntData()); + GlobalOptions.err.print + (" " + slot + " " + instr.getIncrement()); break; } case opc_goto: @@ -433,10 +549,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { case opc_if_icmpgt: case opc_if_icmple: case opc_if_acmpeq: case opc_if_acmpne: case opc_ifnull: case opc_ifnonnull: - instr.replaceInstruction(opcode); - instr.setLength(3); + instr = new Instruction(opcode); + length = 3; succAddrs[addr] = new int[] { addr+input.readShort() }; - predcounts[succAddrs[addr][0]]++; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+succAddrs[addr][0]); @@ -444,50 +559,61 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { case opc_goto_w: case opc_jsr_w: - instr.replaceInstruction(opcode - (opc_goto_w - opc_goto)); - instr.setLength(5); + instr = new Instruction(opcode - (opc_goto_w - opc_goto)); + length = 5; succAddrs[addr] = new int[] { addr+input.readInt() }; - predcounts[succAddrs[addr][0]]++; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+succAddrs[addr][0]); break; case opc_tableswitch: { - int length = 3-(addr % 4); + length = 3 - (addr % 4); input.readFully(new byte[length]); int def = input.readInt(); int low = input.readInt(); int high = input.readInt(); - instr.replaceInstruction(opcode); - instr.setIntData(low); - succAddrs[addr] = new int[high-low+2]; - for (int i=0; i+low <= high; i++) { - succAddrs[addr][i] = addr + input.readInt(); - predcounts[succAddrs[addr][i]]++; + 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++; } - succAddrs[addr][high-low+1] = addr + def; - predcounts[addr + def]++; - instr.setLength(length + 13 + 4 * (high-low+1)); + 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: { - int length = 3-(addr % 4); + length = 3 - (addr % 4); input.readFully(new byte[length]); int def = input.readInt(); int npairs = input.readInt(); - instr.replaceInstruction(opcode); + 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(); - predcounts[succAddrs[addr][i]]++; } succAddrs[addr][npairs] = addr + def; - predcounts[addr + def]++; instr.setValues(values); - instr.setLength(length + 9 + 8 * npairs); + length += 9 + 8 * npairs; break; } @@ -515,9 +641,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { || opcode != opc_invokespecial)) throw new ClassFormatException ("Illegal call of special method/field "+ref); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setReference(ref); - instr.setLength(3); + length = 3; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+ref); @@ -542,9 +668,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { throw new ClassFormatException ("Interface reserved param not zero"); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setReference(ref); - instr.setLength(5); + length = 5; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+ref); @@ -558,9 +684,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (opcode == opc_new && type.charAt(0) == '[') throw new ClassFormatException ("Can't create array with opc_new"); - instr.replaceInstruction(opcode); + instr = new Instruction(opcode); instr.setClazzType(type); - instr.setLength(3); + length = 3; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+type); @@ -569,7 +695,10 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { case opc_multianewarray: { String type = cp.getClassType(input.readUnsignedShort()); int dims = input.readUnsignedByte(); - for (int i=0; i < dims; i++) + 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. @@ -577,12 +706,12 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { */ if (type.charAt(i) != '[') throw new ClassFormatException - ("multianewarray called for non array:" - + instr.getDescription()); - instr.replaceInstruction(opcode); + ("multianewarray called for non array:"+ type); + } + instr = new Instruction(opcode); instr.setClazzType(type); - instr.setIntData(dims); - instr.setLength(4); + instr.setDimensions(dims); + length = 4; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" " + type + " " + dims); @@ -590,10 +719,10 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { } case opc_anewarray: { String type = cp.getClassType(input.readUnsignedShort()); - instr.replaceInstruction(opc_multianewarray); + instr = new Instruction(opc_multianewarray); instr.setClazzType(("["+type).intern()); - instr.setIntData(1); - instr.setLength(3); + instr.setDimensions(1); + length = 3; if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+type); @@ -606,10 +735,10 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.print(" "+type); - instr.replaceInstruction(opc_multianewarray); + instr = new Instruction(opc_multianewarray); instr.setClazzType(type); - instr.setIntData(1); - instr.setLength(2); + instr.setDimensions(1); + length = 2; break; } @@ -649,8 +778,8 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { case opc_athrow: case opc_arraylength: case opc_monitorenter: case opc_monitorexit: - instr.replaceInstruction(opcode); - instr.setLength(1); + instr = new Instruction(opcode); + length = 1; break; default: throw new ClassFormatError("Invalid opcode "+opcode); @@ -658,23 +787,32 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_BYTECODE) != 0) GlobalOptions.err.println(); - addr += instr.getLength(); + + instrs[addr] = instr; + instructions.add(instr); + + addr += length; + instructions.setLastAddr(addr); } + if (addr != codeLength) + throw new ClassFormatError("last instruction too long"); } - for (Instruction instr = firstInstr; - instr != null; instr = instr.getNextByAddr()) { + 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; - instr.succs = new Instruction[length]; + Instruction[] succs = new Instruction[length]; for (int i=0; i < length; i++) { int succAddr = succAddrs[addr][i]; - instr.succs[i] = instrs[succAddr]; - if (instr.succs[i].preds == null) - instr.succs[i].preds - = new Instruction[predcounts[succAddr]]; - instr.succs[i].preds[--predcounts[succAddr]] = instr; + 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; @@ -699,20 +837,21 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { } public void dumpCode(java.io.PrintWriter output) { - for (Instruction instr = firstInstr; - instr != null; instr = instr.getNextByAddr()) { + for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { + Instruction instr = (Instruction) iter.next(); output.println(instr.getDescription() + " " + Integer.toHexString(hashCode())); - if (instr.succs != null) { - output.print("\tsuccs: "+instr.succs[0]); - for (int i = 1; i < instr.succs.length; i++) - output.print(", "+instr.succs[i]); + 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.preds != null) { - output.print("\tpreds: " + instr.preds[0]); - for (int i=1; i < instr.preds.length; i++) - output.print(", " + instr.preds[i]); + 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(); } } @@ -726,8 +865,8 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { public void reserveSmallConstants(GrowableConstantPool gcp) { next_instr: - for (Instruction instr = firstInstr; - instr != null; instr = instr.getNextByAddr()) { + for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { + Instruction instr = (Instruction) iter.next(); if (instr.getOpcode() == opc_ldc) { Object constant = instr.getConstant(); if (constant == null) @@ -750,8 +889,8 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { public void prepareWriting(GrowableConstantPool gcp) { /* Recalculate addr, length and add all constants to gcp */ int addr = 0; - for (Instruction instr = firstInstr; - instr != null; instr = instr.getNextByAddr()) { + for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { + Instruction instr = (Instruction) iter.next(); int opcode = instr.getOpcode(); instr.setAddr(addr); int length; @@ -794,42 +933,51 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { } 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: - case opc_ret: - case opc_iinc: { - int slot = instr.getLocalSlot(); - if (opcode == opc_iinc) { - if (slot < 256 - && instr.getIntData() >= Byte.MIN_VALUE - && instr.getIntData() <= Byte.MAX_VALUE) - length = 3; - else - length = 6; - } else { - if (opcode != opc_ret && slot < 4) - length = 1; - else if (slot < 256) - length = 2; - else - length = 4; + if (instr.getLocalSlot() < 4) { + length = 1; + break; } + /* fall through */ + case opc_ret: { + if (instr.getLocalSlot() < 256) + length = 2; + else + length = 4; break; } - case opc_tableswitch: { - length = 3-(addr % 4); - length += 9 + 4 * instr.succs.length; - break; - } case opc_lookupswitch: { length = 3-(addr % 4); - length += 1 + 8 * instr.succs.length; + 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.succs[0].getAddr() - instr.getAddr(); + int dist = instr.getSingleSucc().getAddr() - instr.getAddr(); if (dist < Short.MIN_VALUE || dist > Short.MAX_VALUE) { /* wide goto / jsr */ length = 5; @@ -848,7 +996,7 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { length = 3; break; case opc_multianewarray: { - if (instr.getIntData() == 1) { + if (instr.getDimensions() == 1) { String clazz = instr.getClazzType().substring(1); if (newArrayTypes.indexOf(clazz.charAt(0)) != -1) { length = 2; @@ -926,10 +1074,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { default: throw new ClassFormatError("Invalid opcode "+opcode); } - instr.setLength(length); addr += length; } - codeLength = addr; + instructions.setLastAddr(addr); for (int i=0; i< exceptionHandlers.length; i++) if (exceptionHandlers[i].type != null) gcp.putClassName(exceptionHandlers[i].type); @@ -989,9 +1136,9 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { DataOutputStream output) throws IOException { output.writeShort(maxStack); output.writeShort(maxLocals); - output.writeInt(codeLength); - for (Instruction instr = firstInstr; - instr != null; instr = instr.getNextByAddr()) { + output.writeInt(instructions.getCodeLength()); + for (Iterator iter = instructions.iterator(); iter.hasNext(); ) { + Instruction instr = (Instruction) iter.next(); int opcode = instr.getOpcode(); switch_opc: switch (opcode) { @@ -1077,7 +1224,7 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { } case opc_iinc: { int slot = instr.getLocalSlot(); - int incr = instr.getIntData(); + int incr = instr.getIncrement(); if (instr.getLength() == 3) { output.writeByte(opcode); output.writeByte(slot); @@ -1095,7 +1242,7 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { if (instr.getLength() == 5) { /* wide goto or jsr */ output.writeByte(opcode + (opc_goto_w - opc_goto)); - output.writeInt(instr.succs[0].getAddr() + output.writeInt(instr.getSingleSucc().getAddr() - instr.getAddr()); break; } @@ -1109,37 +1256,49 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { case opc_if_acmpeq: case opc_if_acmpne: case opc_ifnull: case opc_ifnonnull: output.writeByte(opcode); - output.writeShort(instr.succs[0].getAddr() + output.writeShort(instr.getSingleSucc().getAddr() - instr.getAddr()); break; - case opc_tableswitch: { - output.writeByte(opcode); - int align = 3-(instr.getAddr() % 4); - int numcases = instr.succs.length - 1; - output.write(new byte[align]); - /* def */ - output.writeInt(instr.succs[numcases].getAddr() - instr.getAddr()); - /* low */ - output.writeInt(instr.getIntData()); - /* high */ - output.writeInt(instr.getIntData() + numcases - 1); - for (int i=0; i < numcases; i++) - output.writeInt(instr.succs[i].getAddr() - instr.getAddr()); - break; - } case opc_lookupswitch: { - output.writeByte(opcode); - int[] values = instr.getValues(); int align = 3-(instr.getAddr() % 4); - int numcases = values.length; + 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(instr.succs[numcases].getAddr() - instr.getAddr()); - output.writeInt(numcases); - for (int i=0; i < numcases; i++) { + output.writeInt(defAddr); + output.writeInt(npairs); + for (int i=0; i < npairs; i++) { output.writeInt(values[i]); - output.writeInt(instr.succs[i].getAddr() - instr.getAddr()); + output.writeInt(instr.getSuccs()[i].getAddr() + -instr.getAddr()); } break; } @@ -1175,7 +1334,7 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { output.writeShort(gcp.putClassType(instr.getClazzType())); break; case opc_multianewarray: - if (instr.getIntData() == 1) { + if (instr.getDimensions() == 1) { String clazz = instr.getClazzType().substring(1); int index = newArrayTypes.indexOf(clazz.charAt(0)); if (index != -1) { @@ -1188,7 +1347,7 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { } else { output.writeByte(opcode); output.writeShort(gcp.putClassType(instr.getClazzType())); - output.writeByte(instr.getIntData()); + output.writeByte(instr.getDimensions()); } break; @@ -1268,7 +1427,8 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { size += 8 + lvt.length * 10; if (lnt != null) size += 8 + lnt.length * 4; - return 10 + codeLength + exceptionHandlers.length * 8 + return 10 + instructions.getCodeLength() + + exceptionHandlers.length * 8 + getAttributeSize() + size; } @@ -1284,40 +1444,10 @@ public class BytecodeInfo extends BinaryInfo implements Opcodes { return methodInfo; } - public Collection getInstructions() { - return new AbstractCollection() { - public int size() { - return instructionCount; - } - - public Iterator iterator() { - return new Iterator() { - Instruction instr = firstInstr; - - public boolean hasNext() { - return instr != null; - } - - public Object next() { - if (instr == null) - throw new NoSuchElementException(); - Instruction result = instr; - instr = instr.getNextByAddr(); - return result; - } - - public void remove() { - instr.getPrevByAddr().removeInstruction(); - } - }; - } - }; + public List getInstructions() { + return instructions; } - public Instruction getFirstInstr() { - return firstInstr; - } - public Handler[] getExceptionHandlers() { return exceptionHandlers; } diff --git a/jode/jode/bytecode/ClassInfo.java.in b/jode/jode/bytecode/ClassInfo.java.in index 81e2fd1..d612a23 100644 --- a/jode/jode/bytecode/ClassInfo.java.in +++ b/jode/jode/bytecode/ClassInfo.java.in @@ -19,16 +19,13 @@ 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; -///#ifdef JDK12 -///import java.lang.ref.WeakReference; -///import java.lang.ref.ReferenceQueue; -///#endif -import @COLLECTIONS@.Map; -import @COLLECTIONS@.HashMap; import @COLLECTIONS@.Iterator; import java.lang.reflect.Constructor; @@ -49,10 +46,12 @@ import java.lang.reflect.Modifier; public class ClassInfo extends BinaryInfo { private static SearchPath classpath; - private static final Map classes = new HashMap(); -///#ifdef JDK12 -/// private static final ReferenceQueue queue = new ReferenceQueue(); -///#endif + + private static final UnifyHash classes = new UnifyHash(); +// private static final Map classes = new HashMap(); +// ///#ifdef JDK12 +// /// private static final ReferenceQueue queue = new ReferenceQueue(); +// ///#endif private int status = 0; @@ -70,26 +69,12 @@ public class ClassInfo extends BinaryInfo { private String sourceFile; public final static ClassInfo javaLangObject = forName("java.lang.Object"); - + public static void setClassPath(String path) { classpath = new SearchPath(path); -///#ifdef JDK12 -/// java.lang.ref.Reference died; -/// while ((died = queue.poll()) != null) { -/// classes.values().remove(died); -/// } -/// Iterator i = classes.values().iterator(); -/// while (i.hasNext()) { -/// ClassInfo ci = (ClassInfo) ((WeakReference)i.next()).get(); -/// if (ci == null) { -/// i.remove(); -/// continue; -/// } -///#else - Iterator i = classes.values().iterator(); + Iterator i = classes.iterator(); while (i.hasNext()) { ClassInfo ci = (ClassInfo) i.next(); -///#endif ci.status = 0; ci.superclass = null; ci.fields = null; @@ -124,7 +109,7 @@ public class ClassInfo extends BinaryInfo { } }; } - + public static ClassInfo forName(String name) { if (name == null || name.indexOf(';') != -1 @@ -132,24 +117,15 @@ public class ClassInfo extends BinaryInfo { || name.indexOf('/') != -1) throw new IllegalArgumentException("Illegal class name: "+name); -///#ifdef JDK12 -/// java.lang.ref.Reference died; -/// while ((died = queue.poll()) != null) { -/// classes.values().remove(died); -/// } -/// WeakReference ref = (WeakReference) classes.get(name); -/// ClassInfo clazz = (ref == null) ? null : (ClassInfo) ref.get(); -///#else - ClassInfo clazz = (ClassInfo) classes.get(name); -///#endif - if (clazz == null) { - clazz = new ClassInfo(name); -///#ifdef JDK12 -/// classes.put(name, new WeakReference(clazz, queue)); -///#else - classes.put(name, clazz); -///#endif - } + 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; } @@ -565,9 +541,9 @@ public class ClassInfo extends BinaryInfo { return; } try { - DataInputStream input = - new DataInputStream(classpath.getFile(name.replace('.', '/') - + ".class")); + DataInputStream input = new DataInputStream + (new BufferedInputStream + (classpath.getFile(name.replace('.', '/') + ".class"))); read(input, howMuch); } catch (IOException ex) { @@ -602,6 +578,7 @@ public class ClassInfo extends BinaryInfo { ("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; diff --git a/jode/jode/bytecode/Instruction.java b/jode/jode/bytecode/Instruction.java index f65c9d2..c446e86 100644 --- a/jode/jode/bytecode/Instruction.java +++ b/jode/jode/bytecode/Instruction.java @@ -18,27 +18,29 @@ */ package jode.bytecode; -import java.util.Vector; -import java.util.Enumeration; /** * This class represents an instruction in the byte code. * */ public final class Instruction implements Opcodes{ - private BytecodeInfo codeinfo; /** * The opcode of the instruction. We map some opcodes, e.g. *
      * iload_[0-3] -> iload, ldc_w -> ldc, wide iinc -> iinc.
      * 
*/ + // 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. This info is - * used when swapping locals. + * If this opcode uses a local this gives the slot. For multianewarray + * this gives the dimension. */ - private int localSlot = -1; + 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: @@ -54,68 +56,50 @@ public final class Instruction implements Opcodes{ * */ private Object objData; - /** - * 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_tableswitch
- *
The start value of the table
- *
opc_multianewarray
- *
The number of dimensions (1..255)
- *
opc_lookupswitch
- *
The array of values of type int[]
- *
- */ - private int intData; - /** - * The address of this opcode. - */ - private int addr; - /** - * The length of this opcode. You shouldn't touch it, nor rely on - * it, since the length of some opcodes may change automagically - * (e.g. when changing localSlot iload_0 <-> iload 5) - */ - private int length; /** * The successors of this opcodes, where flow may lead to * (except that nextByAddr is implicit if !alwaysJump). The - * value null is equivalent to an empty array. + * value null means no successor, if there is one succesor, this + * is of type Instruction, otherwise, this is an array of Instruction. */ - Instruction[] succs; + private Object succs; /** * The predecessors of this opcode, orthogonal to the succs array. * This must be null or a non empty array. */ - Instruction[] preds; + private Instruction[] preds; /** * The next instruction in code order. */ - private Instruction nextByAddr; + Instruction nextByAddr; /** * The previous instruction in code order, useful when changing * the order. */ - private Instruction prevByAddr; + Instruction prevByAddr; /** * 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! */ private Object tmpInfo; - public Instruction(BytecodeInfo ci) { - this.codeinfo = ci; + public Instruction(int opcode) { + this.opcode = opcode; } /** - * Returns the opcode of the instruction. We map some opcodes, e.g. + * Returns the opcode of the instruction. We map some opcodes: *
-     * iload_0   -> iload
-     * ldc_w     -> ldc
-     * wide iinc -> iinc
+     * [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 int getOpcode() { @@ -137,6 +121,10 @@ public final class Instruction implements Opcodes{ return addr; } + public final int getNextAddr() { + return nextByAddr.addr; + } + /** * Returns the length of this opcode. See getAddr() for some * notes. Note that the length doesn't necessarily reflect the @@ -145,38 +133,88 @@ public final class Instruction implements Opcodes{ * in constant pool, and the order they are allocated. */ public final int getLength() { - return length; + return getNextAddr() - addr; } final void setAddr(int addr) { this.addr = addr; } - final void setLength(int length) { - this.length = length; - } - public final int getLocalSlot() { - return localSlot; + public final boolean hasLocalSlot() { + return opcode == opc_iinc || opcode == opc_ret + || opcode >= opc_iload && opcode <= opc_aload + || opcode >= opc_istore && opcode <= opc_astore; + } + + public final int getLocalSlot() + /*{ require { hasLocalSlot() + :: "Instruction has no slot" } }*/ + { + return shortData; } - public final void setLocalSlot(int slot) { - localSlot = slot; + public final void setLocalSlot(int slot) + /*{ require { hasLocalSlot() + :: "Instruction has no slot" } }*/ + { + shortData = slot; } - public final int getIntData() + /** + * 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)
+ *
+ */ + public final int getIncrement() /*{ require { opcode == opc_iinc || opcode == opc_multianewarray || opcode == opc_tableswitch :: "Instruction has no int data" } }*/ { - return intData; + /* shortData already used for local slot */ + return ((Short) objData).shortValue(); } - public final void setIntData(int data) + /** + * 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)
+ *
+ */ + public final void setIncrement(int incr) /*{ require { opcode == opc_iinc || opcode == opc_multianewarray - || opcode == opc_tableswitch :: "Instruction has no int data" } }*/ { - this.intData = data; + /* shortData already used for local slot */ + objData = new Short((short) incr); + } + + /** + * + */ + public final int getDimensions() + /*{ require { opcode == opc_multianewarray + :: "Instruction has no dimensions" } }*/ + { + return shortData; + } + + /** + * + */ + public final void setDimensions(int dims) + /*{ require { opcode == opc_multianewarray + :: "Instruction has no dimensions" } }*/ + { + shortData = dims; } public final Object getConstant() @@ -265,15 +303,44 @@ public final class Instruction implements Opcodes{ 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() { - return succs; + 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_xxxunusedxxx) + return null; return prevByAddr; } public final Instruction getNextByAddr() { + if (nextByAddr.opcode == opc_xxxunusedxxx) + return null; return nextByAddr; } @@ -285,34 +352,68 @@ public final class Instruction implements Opcodes{ tmpInfo = info; } - public final void replaceInstruction(int newOpcode) { - replaceInstruction(newOpcode, null); + + // 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; } - public final void replaceInstruction(int newOpcode, - Instruction[] newSuccs) { - if (succs != null && succs != newSuccs) { - for (int i = 0; i< succs.length; i++) - succs[i].removePredecessor(this); + /** + * @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; } - - opcode = newOpcode; - localSlot = -1; - objData = null; - intData = 0; - if (succs != newSuccs) - setSuccs(newSuccs); } - private final void setSuccs(Instruction[] newSuccs) { - succs = newSuccs; - if (succs != null) { - for (int i = 0; i< succs.length; i++) - succs[i].addPredecessor(this); + /** + * @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); } } - public void addPredecessor(Instruction pred) { + void addPredecessor(Instruction pred) { if (preds == null) { preds = new Instruction[] { pred }; return; @@ -324,7 +425,7 @@ public final class Instruction implements Opcodes{ preds = newPreds; } - public void removePredecessor(Instruction pred) { + void removePredecessor(Instruction pred) { /* Hopefully it doesn't matter if this is slow */ int predLength = preds.length; if (predLength == 1) { @@ -342,108 +443,107 @@ public final class Instruction implements Opcodes{ } } - public final Instruction insertInstruction(int opc) { - codeinfo.instructionCount++; - Instruction newInstr = new Instruction(codeinfo); - newInstr.opcode = opc; - newInstr.addr = addr; + // 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; - if (prevByAddr != null) - prevByAddr.nextByAddr = newInstr; - else - codeinfo.firstInstr = newInstr; - newInstr.nextByAddr = this; - prevByAddr = newInstr; - - /* promote the predecessors to newInstr */ + prevByAddr = null; + nextByAddr = null; + + /* promote the successors of the predecessors to newInstr */ if (preds != null) { for (int j=0; j < preds.length; j++) - for (int i=0; i < preds[j].succs.length; i++) - if (preds[j].succs[i] == this) - preds[j].succs[i] = newInstr; + preds[j].promoteSuccs(this, newInstr); newInstr.preds = preds; preds = null; } - return newInstr; - } - public final Instruction insertInstruction(int opc, - Instruction[] newSuccs) { - Instruction newInstr = insertInstruction(opc); - if (newSuccs != null) - newInstr.setSuccs(newSuccs); - return newInstr; + /* 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; + } + } } - public Instruction appendInstruction(int opc) { - codeinfo.instructionCount++; - Instruction newInstr = new Instruction(codeinfo); - newInstr.opcode = opc; - newInstr.addr = addr + length; - newInstr.length = 0; + void appendInstruction(Instruction newInstr) { + newInstr.addr = nextByAddr.addr; + newInstr.nextByAddr = nextByAddr; - if (nextByAddr != null) - nextByAddr.prevByAddr = newInstr; + nextByAddr.prevByAddr = newInstr; newInstr.prevByAddr = this; - nextByAddr = newInstr; - return newInstr; - } - - public Instruction appendInstruction(int opc, Instruction[] newSuccs) { - Instruction newInstr = appendInstruction(opc); - if (newSuccs != null) - newInstr.setSuccs(newSuccs); - return newInstr; } /** * Removes this instruction (as if it would be replaced by a nop). */ - public void removeInstruction() { - codeinfo.instructionCount--; + void removeInstruction(BytecodeInfo codeinfo) { /* remove from chained list and adjust addr / length */ - if (prevByAddr != null) { - prevByAddr.nextByAddr = nextByAddr; - prevByAddr.length += length; - } else { - if (nextByAddr == null) - /* Mustn't happen, each method must include a return */ - throw new IllegalArgumentException - ("Removing the last instruction of a method!"); - codeinfo.firstInstr = nextByAddr; - nextByAddr.addr = 0; - nextByAddr.length += length; - } - - if (nextByAddr != null) - nextByAddr.prevByAddr = prevByAddr; + prevByAddr.nextByAddr = nextByAddr; + nextByAddr.prevByAddr = prevByAddr; /* remove predecessors of successors */ - if (succs != null) { - for (int i=0; i < succs.length; i++) - succs[i].removePredecessor(this); - succs = null; - } + removeSuccs(); - Instruction alternative = nextByAddr != null ? nextByAddr : prevByAddr; - /* remove the predecessors to alternative */ + /* promote the predecessors to next instruction */ if (preds != null) { for (int j=0; j < preds.length; j++) - for (int i=0; i < preds[j].succs.length; i++) - if (preds[j].succs[i] == this) - preds[j].succs[i] = alternative; - if (alternative.preds == null) - alternative.preds = preds; + preds[j].promoteSuccs(this, nextByAddr); + if (nextByAddr.preds == null) + nextByAddr.preds = preds; else { - Instruction[] newPreds - = new Instruction[alternative.preds.length + preds.length]; - System.arraycopy(preds, 0, newPreds, 0, preds.length); - System.arraycopy(alternative.preds, 0, newPreds, preds.length, - alternative.preds.length); - alternative.preds = newPreds; + 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; } @@ -499,7 +599,7 @@ public final class Instruction implements Opcodes{ if (lnt != null) { for (int i=0; i< lnt.length; i++) { if (lnt[i].start == this) { - if (nextByAddr == null + if (nextByAddr.opcode == opc_xxxunusedxxx || (i+1 < lnt.length && lnt[i+1].start == nextByAddr)) { /* Remove the line number. @@ -517,6 +617,22 @@ public final class Instruction implements Opcodes{ } } } + + 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; } /** @@ -541,7 +657,7 @@ public final class Instruction implements Opcodes{ case opc_invokespecial: case opc_invokestatic: case opc_invokeinterface: { - Reference ref = (Reference) objData; + Reference ref = getReference(); String typeSig = ref.getType(); poppush[0] = opcode != opc_invokestatic ? 1 : 0; poppush[0] += TypeSignature.getArgumentSize(typeSig); @@ -551,7 +667,7 @@ public final class Instruction implements Opcodes{ case opc_putfield: case opc_putstatic: { - Reference ref = (Reference) objData; + Reference ref = getReference(); poppush[1] = 0; poppush[0] = TypeSignature.getTypeSize(ref.getType()); if (opcode == opc_putfield) @@ -560,7 +676,7 @@ public final class Instruction implements Opcodes{ } case opc_getstatic: case opc_getfield: { - Reference ref = (Reference) objData; + Reference ref = getReference(); poppush[1] = TypeSignature.getTypeSize(ref.getType()); poppush[0] = opcode == opc_getfield ? 1 : 0; break; @@ -568,7 +684,7 @@ public final class Instruction implements Opcodes{ case opc_multianewarray: { poppush[1] = 1; - poppush[0] = prevByAddr.intData; + poppush[0] = getDimensions(); break; } default: @@ -620,27 +736,24 @@ public final class Instruction implements Opcodes{ StringBuffer result = new StringBuffer(String.valueOf(addr)) .append('_').append(Integer.toHexString(hashCode())) .append(": ").append(opcodeString[opcode]); - if (localSlot != -1) - result.append(" ").append(localSlot); - if (succs != null && succs.length == 1) - result.append(" ").append(succs[0].addr); - switch (opcode) { - case opc_iinc: - result.append(" ").append(intData); - break; - case opc_ldc: case opc_ldc2_w: - case opc_getstatic: case opc_getfield: - case opc_putstatic: case opc_putfield: - case opc_invokespecial: case opc_invokestatic: case opc_invokevirtual: - case opc_new: - case opc_checkcast: - case opc_instanceof: - result.append(" ").append(objData); - break; - case opc_multianewarray: - case opc_invokeinterface: - result.append(" ").append(objData).append(" ").append(intData); - break; + 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(); } diff --git a/jode/jode/bytecode/Reference.java b/jode/jode/bytecode/Reference.java deleted file mode 100644 index 97d2685..0000000 --- a/jode/jode/bytecode/Reference.java +++ /dev/null @@ -1,94 +0,0 @@ -/* Reference 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; -///#ifdef JDK12 -///import java.lang.ref.WeakReference; -///import java.lang.ref.ReferenceQueue; -///import java.lang.ref.HashMap; -///#else -import java.util.Hashtable; -///#endif - -/** - * This class represents a field or method reference. - */ -public class Reference { - /** - * The reference string. This is the class name, the member name and - * the member type, all separated by a space. - */ - private final String sig; - /** - * The position of the first and second space in the reference - * string. - */ - private final int firstSpace, secondSpace; - -///#ifdef JDK12 -/// private static final Map references = new HashMap(); -///#else - private static final Hashtable references = new Hashtable(); -///#endif - - public static Reference getReference(String className, - String name, String type) { - String sig = className+" "+name+" "+type; -///#ifdef JDK12 -/// WeakReference ref = (WeakReference) references.get(sig); -/// Reference reference = (ref == null) ? null : (Reference) ref.get(); -///#else - Reference reference = (Reference) references.get(sig); -///#endif - if (reference == null) { - sig = sig.intern(); - int firstSpace = className.length(); - int secondSpace = firstSpace + name.length() + 1; - reference = new Reference(sig, firstSpace, secondSpace); -///#ifdef JDK12 -/// references.put(sig, new WeakReference(reference)); -///#else - references.put(sig, reference); -///#endif - } - return reference; - } - - private Reference(String sig, int first, int second) { - this.sig = sig; - this.firstSpace = first; - this.secondSpace = second; - } - - public String getClazz() { - return sig.substring(0, firstSpace); - } - - public String getName() { - return sig.substring(firstSpace + 1, secondSpace); - } - - public String getType() { - return sig.substring(secondSpace + 1); - } - - public String toString() { - return sig; - } -} diff --git a/jode/jode/bytecode/Reference.java.in b/jode/jode/bytecode/Reference.java.in new file mode 100644 index 0000000..f2cd03a --- /dev/null +++ b/jode/jode/bytecode/Reference.java.in @@ -0,0 +1,72 @@ +/* Reference 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.UnifyHash; +import @COLLECTIONS@.Iterator; + +/** + * This class represents a field or method reference. + */ +public class Reference { + /** + * A reference consists of a class name, a member name and a type. + */ + private final String clazz, name, type; + + private static final UnifyHash unifier = new UnifyHash(); + + public static Reference getReference(String className, + String name, String type) { + int hash = className.hashCode() ^ name.hashCode() ^ type.hashCode(); + Iterator iter = unifier.iterateHashCode(hash); + while (iter.hasNext()) { + Reference ref = (Reference) iter.next(); + if (ref.clazz.equals(className) + && ref.name.equals(name) + && ref.type.equals(type)) + return ref; + } + Reference ref = new Reference(className, name, type); + unifier.put(hash, ref); + return ref; + } + + private Reference(String clazz, String name, String type) { + this.clazz = clazz; + this.name = name; + this.type = type; + } + + public String getClazz() { + return clazz; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public String toString() { + return clazz + " " + name + " " + type; + } +} diff --git a/jode/jode/bytecode/TypeSignature.java b/jode/jode/bytecode/TypeSignature.java index 136b7c9..700d4bc 100644 --- a/jode/jode/bytecode/TypeSignature.java +++ b/jode/jode/bytecode/TypeSignature.java @@ -80,6 +80,28 @@ public class TypeSignature { public static int getTypeSize(String typeSig) { return usingTwoSlots(typeSig.charAt(0)) ? 2 : 1; } + + public static String getElementType(String typeSig) { + if (typeSig.charAt(0) != '[') + throw new IllegalArgumentException(); + return typeSig.substring(1); + } + + public static ClassInfo getClassInfo(String typeSig) { + if (typeSig.charAt(0) != 'L') + throw new IllegalArgumentException(); + return ClassInfo.forName + (typeSig.substring(1, typeSig.length()-1).replace('/', '.')); + } + + public static int skipType(String methodTypeSig, int position) { + char c = methodTypeSig.charAt(position++); + while (c == '[') + c = methodTypeSig.charAt(position++); + if (c == 'L') + return methodTypeSig.indexOf(';', position) + 1; + return position; + } /** * Returns the number of words, the arguments for the given method @@ -89,18 +111,14 @@ public class TypeSignature { int nargs = 0; int i = 1; for (;;) { - char c = methodTypeSig.charAt(i++); + char c = methodTypeSig.charAt(i); if (c == ')') return nargs; + i = skipType(methodTypeSig, i); if (usingTwoSlots(c)) nargs += 2; - else { - while (c == '[') - c = methodTypeSig.charAt(i++); - if (c == 'L') - i = methodTypeSig.indexOf(';', i) + 1; + else nargs++; - } } } @@ -109,9 +127,44 @@ public class TypeSignature { * signature takes. */ public static int getReturnSize(String methodTypeSig) { - char returnType = methodTypeSig.charAt(methodTypeSig.indexOf(')')+1); - return returnType == 'V' ? 0 - : usingTwoSlots(returnType) ? 2 : 1; + int length = methodTypeSig.length(); + if (methodTypeSig.charAt(length - 2) == ')') { + // This is a single character return type. + char returnType = methodTypeSig.charAt(length - 1); + return returnType == 'V' ? 0 + : usingTwoSlots(returnType) ? 2 : 1; + } else + // All multi character return types take one parameter + return 1; + } + + /** + * Returns the number of words, an object of the given simple type + * signature takes. + */ + public static String[] getParameterTypes(String methodTypeSig) { + int pos = 1; + int count = 0; + while (methodTypeSig.charAt(pos) != ')') { + pos = skipType(methodTypeSig, pos); + count++; + } + String[] params = new String[count]; + pos = 1; + for (int i = 0; i < count; i++) { + int start = pos; + pos = skipType(methodTypeSig, pos); + params[i] = methodTypeSig.substring(start, pos); + } + return params; + } + + /** + * Returns the number of words, an object of the given simple type + * signature takes. + */ + public static String getReturnType(String methodTypeSig) { + return methodTypeSig.substring(methodTypeSig.lastIndexOf(')')+1); } private static void checkClassName(String clName)