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
branch_1_1
jochen 25 years ago
parent ddbf2c969a
commit 996fc49dbd
  1. 30
      jode/jode/bytecode/BinaryInfo.java.in
  2. 584
      jode/jode/bytecode/BytecodeInfo.java.in
  3. 67
      jode/jode/bytecode/ClassInfo.java.in
  4. 457
      jode/jode/bytecode/Instruction.java
  5. 94
      jode/jode/bytecode/Reference.java
  6. 72
      jode/jode/bytecode/Reference.java.in
  7. 69
      jode/jode/bytecode/TypeSignature.java

@ -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,9 +71,12 @@ 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 {
int length;
@ -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,9 +163,12 @@ 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);
if (unknownAttributes != null) {
Iterator i = unknownAttributes.entrySet().iterator();
while (i.hasNext()) {
Map.Entry e = (Map.Entry) i.next();
@ -170,32 +179,43 @@ public class BinaryInfo {
output.write(data);
}
}
}
public int getAttributeSize() {
int size = 2; /* attribute count */
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) {
if (unknownAttributes != null)
return (byte[]) unknownAttributes.get(name);
return null;
}
public Iterator getAttributes() {
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) {
if (unknownAttributes != null)
return (byte[]) unknownAttributes.remove(name);
return null;
}
public void removeAllAttributes() {
unknownAttributes.clear();
unknownAttributes = null;
}
}

@ -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;
private InstructionList instructions;
private class InstructionList extends AbstractSequentialList {
Instruction borderInstr;
int instructionCount = 0;
Handler[] exceptionHandlers;
LocalVariableInfo[] lvt;
LineNumber[] lnt;
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 +
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++;
}
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][high-low+1] = addr + def;
predcounts[addr + def]++;
instr.setLength(length + 13 + 4 * (high-low+1));
}
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_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) {
int increment = instr.getIncrement();
if (slot < 256
&& instr.getIntData() >= Byte.MIN_VALUE
&& instr.getIntData() <= Byte.MAX_VALUE)
&& increment >= Byte.MIN_VALUE
&& increment <= Byte.MAX_VALUE)
length = 3;
else
length = 6;
} else {
if (opcode != opc_ret && slot < 4)
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;
else if (slot < 256)
break;
}
/* fall through */
case opc_ret: {
if (instr.getLocalSlot() < 256)
length = 2;
else
length = 4;
}
break;
}
case opc_tableswitch: {
case opc_lookupswitch: {
length = 3-(addr % 4);
length += 9 + 4 * 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;
}
case opc_lookupswitch: {
length = 3-(addr % 4);
length += 1 + 8 * instr.succs.length;
}
// 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);
case opc_lookupswitch: {
int align = 3-(instr.getAddr() % 4);
int numcases = instr.succs.length - 1;
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(instr.succs[numcases].getAddr() - instr.getAddr());
output.writeInt(defAddr);
/* low */
output.writeInt(instr.getIntData());
output.writeInt(values[0]);
/* high */
output.writeInt(instr.getIntData() + numcases - 1);
for (int i=0; i < numcases; i++)
output.writeInt(instr.succs[i].getAddr() - instr.getAddr());
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;
}
case opc_lookupswitch: {
output.writeByte(opcode);
int[] values = instr.getValues();
int align = 3-(instr.getAddr() % 4);
int numcases = values.length;
}
// 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,38 +1444,8 @@ 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 Instruction getFirstInstr() {
return firstInstr;
public List getInstructions() {
return instructions;
}
public Handler[] getExceptionHandlers() {

@ -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;
@ -73,23 +72,9 @@ public class ClassInfo extends BinaryInfo {
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;
@ -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;

@ -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.
* <pre>
* iload_[0-3] -> iload, ldc_w -> ldc, wide iinc -> iinc.
* </pre>
*/
// 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{
* </dl>
*/
private Object objData;
/**
* Optional integer data for this opcode. There are various uses
* for this:
* <dl>
* <dt>opc_iinc</dt>
* <dd>The value by which the constant is increased/decreased. (short)</dd>
* <dt>opc_tableswitch</dt>
* <dd>The start value of the table</dd>
* <dt>opc_multianewarray</dt>
* <dd>The number of dimensions (1..255)</dd>
* <dt>opc_lookupswitch</dt>
* <dd>The array of values of type int[]</dd>
* </dl>
*/
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:
* <pre>
* iload_0 -&gt; iload
* ldc_w -&gt; ldc
* wide iinc -&gt; iinc
* [iflda]load_x -&gt; [iflda]load
* [iflda]store_x -&gt; [iflda]store
* [ifa]const_xx, ldc_w -&gt; ldc
* [dl]const_xx -&gt; ldc2_w
* wide opcode -&gt; opcode
* tableswitch -&gt; lookupswitch
* [a]newarray -&gt; multianewarray
* </pre>
*/
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 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() {
return localSlot;
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:
* <dl>
* <dt>opc_iinc</dt>
* <dd>The value by which the constant is increased/decreased. (short)</dd>
* <dt>opc_multianewarray</dt>
* <dd>The number of dimensions (1..255)</dd>
* </dl>
*/
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:
* <dl>
* <dt>opc_iinc</dt>
* <dd>The value by which the constant is increased/decreased. (short)</dd>
* <dt>opc_multianewarray</dt>
* <dd>The number of dimensions (1..255)</dd>
* </dl>
*/
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);
}
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);
// 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;
}
opcode = newOpcode;
localSlot = -1;
objData = null;
intData = 0;
if (succs != newSuccs)
setSuccs(newSuccs);
/**
* @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;
}
}
private final void setSuccs(Instruction[] newSuccs) {
/**
* @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;
if (succs != null) {
for (int i = 0; i< succs.length; i++)
succs[i].addPredecessor(this);
((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
newInstr.prevByAddr = prevByAddr;
if (prevByAddr != null)
/**
* Replaces the opcode of this instruction. You should only use the
* mapped opcodes:
* <pre>
* [iflda]load_x -&gt; [iflda]load
* [iflda]store_x -&gt; [iflda]store
* [ifa]const_xx, ldc_w -&gt; ldc
* [dl]const_xx -&gt; ldc2_w
* wide opcode -&gt; opcode
* tableswitch -&gt; lookupswitch
* [a]newarray -&gt; multianewarray
* </pre>
*/
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;
else
codeinfo.firstInstr = newInstr;
newInstr.nextByAddr = this;
prevByAddr = newInstr;
newInstr.prevByAddr = prevByAddr;
prevByAddr = null;
nextByAddr = null;
/* promote the predecessors to newInstr */
/* 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;
/* 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;
}
public final Instruction insertInstruction(int opc,
Instruction[] newSuccs) {
Instruction newInstr = insertInstruction(opc);
if (newSuccs != null)
newInstr.setSuccs(newSuccs);
return 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) {
newInstr.addr = nextByAddr.addr;
public Instruction appendInstruction(int opc) {
codeinfo.instructionCount++;
Instruction newInstr = new Instruction(codeinfo);
newInstr.opcode = opc;
newInstr.addr = addr + length;
newInstr.length = 0;
newInstr.nextByAddr = nextByAddr;
if (nextByAddr != null)
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;
/* 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();
}

@ -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;
}
}

@ -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;
}
}

@ -81,6 +81,28 @@ public class TypeSignature {
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
* type signature takes.
@ -89,29 +111,60 @@ 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++;
}
}
}
/**
* Returns the number of words, an object of the given simple type
* signature takes.
*/
public static int getReturnSize(String methodTypeSig) {
char returnType = methodTypeSig.charAt(methodTypeSig.indexOf(')')+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)

Loading…
Cancel
Save