CodeWriterpublic class CodeWriter extends Object implements CodeVisitorA {@link CodeVisitor CodeVisitor} that generates Java bytecode instructions.
Each visit method of this class appends the bytecode corresponding to the
visited instruction to a byte vector, in the order these methods are called. |
Fields Summary |
---|
static final boolean | CHECKtrue if preconditions must be checked at runtime or not. | CodeWriter | nextNext code writer (see {@link ClassWriter#firstMethod firstMethod}). | private ClassWriter | cwThe class writer to which this method must be added. | private int | nameThe index of the constant pool item that contains the name of this method. | private int | descThe index of the constant pool item that contains the descriptor of this
method. | private int | accessAccess flags of this method. | private int | maxStackMaximum stack size of this method. | private int | maxLocalsMaximum number of local variables for this method. | private ByteVector | codeThe bytecode of this method. | private int | catchCountNumber of entries in the catch table of this method. | private ByteVector | catchTableThe catch table of this method. | private int | exceptionCountNumber of exceptions that can be thrown by this method. | private int[] | exceptionsThe exceptions that can be thrown by this method. More
precisely, this array contains the indexes of the constant pool items
that contain the internal names of these exception classes. | private Attribute | attrsThe non standard attributes of the method. | private int | localVarCountNumber of entries in the LocalVariableTable attribute. | private ByteVector | localVarThe LocalVariableTable attribute. | private int | lineNumberCountNumber of entries in the LineNumberTable attribute. | private ByteVector | lineNumberThe LineNumberTable attribute. | private Attribute | cattrsThe non standard attributes of the method's code. | private boolean | resizeIndicates if some jump instructions are too small and need to be resized. | private final boolean | computeMaxstrue if the maximum stack size and number of local variables must
be automatically computed. | private int | stackSizeThe (relative) stack size after the last visited instruction. This size is
relative to the beginning of the current basic block, i.e., the true stack
size after the last visited instruction is equal to the {@link
Label#beginStackSize beginStackSize} of the current basic block plus
stackSize. | private int | maxStackSizeThe (relative) maximum stack size after the last visited instruction. This
size is relative to the beginning of the current basic block, i.e., the
true maximum stack size after the last visited instruction is equal to the
{@link Label#beginStackSize beginStackSize} of the current basic block plus
stackSize. | private Label | currentBlockThe current basic block. This block is the basic block to which the next
instruction to be visited must be added. | private Label | blockStackThe basic block stack used by the control flow analysis algorithm. This
stack is represented by a linked list of {@link Label Label} objects,
linked to each other by their {@link Label#next} field. This stack must
not be confused with the JVM stack used to execute the JVM instructions! | private static final int[] | SIZEThe stack size variation corresponding to each JVM instruction. This stack
variation is equal to the size of the values produced by an instruction,
minus the size of the values consumed by this instruction. | private Edge | headThe head of the list of {@link Edge Edge} objects used by this {@link
CodeWriter CodeWriter}. These objects, linked to each other by their
{@link Edge#poolNext} field, are added back to the shared pool at the
end of the control flow analysis algorithm. | private Edge | tailThe tail of the list of {@link Edge Edge} objects used by this {@link
CodeWriter CodeWriter}. These objects, linked to each other by their
{@link Edge#poolNext} field, are added back to the shared pool at the
end of the control flow analysis algorithm. | private static Edge | poolThe shared pool of {@link Edge Edge} objects. This pool is a linked list
of Edge objects, linked to each other by their {@link Edge#poolNext} field. |
Constructors Summary |
---|
protected CodeWriter(ClassWriter cw, boolean computeMaxs)Constructs a CodeWriter.
// --------------------------------------------------------------------------
// Static initializer
// --------------------------------------------------------------------------
int i;
int[] b = new int[202];
String s =
"EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDDCDCDEEEEEEEEE" +
"EEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCDCDCEEEEDDDDDDDCDCDCEFEF" +
"DDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFEDDDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
for (i = 0; i < b.length; ++i) {
b[i] = s.charAt(i) - 'E";
}
SIZE = b;
/* code to generate the above string
int NA = 0; // not applicable (unused opcode or variable size opcode)
b = new int[] {
0, //NOP, // visitInsn
1, //ACONST_NULL, // -
1, //ICONST_M1, // -
1, //ICONST_0, // -
1, //ICONST_1, // -
1, //ICONST_2, // -
1, //ICONST_3, // -
1, //ICONST_4, // -
1, //ICONST_5, // -
2, //LCONST_0, // -
2, //LCONST_1, // -
1, //FCONST_0, // -
1, //FCONST_1, // -
1, //FCONST_2, // -
2, //DCONST_0, // -
2, //DCONST_1, // -
1, //BIPUSH, // visitIntInsn
1, //SIPUSH, // -
1, //LDC, // visitLdcInsn
NA, //LDC_W, // -
NA, //LDC2_W, // -
1, //ILOAD, // visitVarInsn
2, //LLOAD, // -
1, //FLOAD, // -
2, //DLOAD, // -
1, //ALOAD, // -
NA, //ILOAD_0, // -
NA, //ILOAD_1, // -
NA, //ILOAD_2, // -
NA, //ILOAD_3, // -
NA, //LLOAD_0, // -
NA, //LLOAD_1, // -
NA, //LLOAD_2, // -
NA, //LLOAD_3, // -
NA, //FLOAD_0, // -
NA, //FLOAD_1, // -
NA, //FLOAD_2, // -
NA, //FLOAD_3, // -
NA, //DLOAD_0, // -
NA, //DLOAD_1, // -
NA, //DLOAD_2, // -
NA, //DLOAD_3, // -
NA, //ALOAD_0, // -
NA, //ALOAD_1, // -
NA, //ALOAD_2, // -
NA, //ALOAD_3, // -
-1, //IALOAD, // visitInsn
0, //LALOAD, // -
-1, //FALOAD, // -
0, //DALOAD, // -
-1, //AALOAD, // -
-1, //BALOAD, // -
-1, //CALOAD, // -
-1, //SALOAD, // -
-1, //ISTORE, // visitVarInsn
-2, //LSTORE, // -
-1, //FSTORE, // -
-2, //DSTORE, // -
-1, //ASTORE, // -
NA, //ISTORE_0, // -
NA, //ISTORE_1, // -
NA, //ISTORE_2, // -
NA, //ISTORE_3, // -
NA, //LSTORE_0, // -
NA, //LSTORE_1, // -
NA, //LSTORE_2, // -
NA, //LSTORE_3, // -
NA, //FSTORE_0, // -
NA, //FSTORE_1, // -
NA, //FSTORE_2, // -
NA, //FSTORE_3, // -
NA, //DSTORE_0, // -
NA, //DSTORE_1, // -
NA, //DSTORE_2, // -
NA, //DSTORE_3, // -
NA, //ASTORE_0, // -
NA, //ASTORE_1, // -
NA, //ASTORE_2, // -
NA, //ASTORE_3, // -
-3, //IASTORE, // visitInsn
-4, //LASTORE, // -
-3, //FASTORE, // -
-4, //DASTORE, // -
-3, //AASTORE, // -
-3, //BASTORE, // -
-3, //CASTORE, // -
-3, //SASTORE, // -
-1, //POP, // -
-2, //POP2, // -
1, //DUP, // -
1, //DUP_X1, // -
1, //DUP_X2, // -
2, //DUP2, // -
2, //DUP2_X1, // -
2, //DUP2_X2, // -
0, //SWAP, // -
-1, //IADD, // -
-2, //LADD, // -
-1, //FADD, // -
-2, //DADD, // -
-1, //ISUB, // -
-2, //LSUB, // -
-1, //FSUB, // -
-2, //DSUB, // -
-1, //IMUL, // -
-2, //LMUL, // -
-1, //FMUL, // -
-2, //DMUL, // -
-1, //IDIV, // -
-2, //LDIV, // -
-1, //FDIV, // -
-2, //DDIV, // -
-1, //IREM, // -
-2, //LREM, // -
-1, //FREM, // -
-2, //DREM, // -
0, //INEG, // -
0, //LNEG, // -
0, //FNEG, // -
0, //DNEG, // -
-1, //ISHL, // -
-1, //LSHL, // -
-1, //ISHR, // -
-1, //LSHR, // -
-1, //IUSHR, // -
-1, //LUSHR, // -
-1, //IAND, // -
-2, //LAND, // -
-1, //IOR, // -
-2, //LOR, // -
-1, //IXOR, // -
-2, //LXOR, // -
0, //IINC, // visitIincInsn
1, //I2L, // visitInsn
0, //I2F, // -
1, //I2D, // -
-1, //L2I, // -
-1, //L2F, // -
0, //L2D, // -
0, //F2I, // -
1, //F2L, // -
1, //F2D, // -
-1, //D2I, // -
0, //D2L, // -
-1, //D2F, // -
0, //I2B, // -
0, //I2C, // -
0, //I2S, // -
-3, //LCMP, // -
-1, //FCMPL, // -
-1, //FCMPG, // -
-3, //DCMPL, // -
-3, //DCMPG, // -
-1, //IFEQ, // visitJumpInsn
-1, //IFNE, // -
-1, //IFLT, // -
-1, //IFGE, // -
-1, //IFGT, // -
-1, //IFLE, // -
-2, //IF_ICMPEQ, // -
-2, //IF_ICMPNE, // -
-2, //IF_ICMPLT, // -
-2, //IF_ICMPGE, // -
-2, //IF_ICMPGT, // -
-2, //IF_ICMPLE, // -
-2, //IF_ACMPEQ, // -
-2, //IF_ACMPNE, // -
0, //GOTO, // -
1, //JSR, // -
0, //RET, // visitVarInsn
-1, //TABLESWITCH, // visiTableSwitchInsn
-1, //LOOKUPSWITCH, // visitLookupSwitch
-1, //IRETURN, // visitInsn
-2, //LRETURN, // -
-1, //FRETURN, // -
-2, //DRETURN, // -
-1, //ARETURN, // -
0, //RETURN, // -
NA, //GETSTATIC, // visitFieldInsn
NA, //PUTSTATIC, // -
NA, //GETFIELD, // -
NA, //PUTFIELD, // -
NA, //INVOKEVIRTUAL, // visitMethodInsn
NA, //INVOKESPECIAL, // -
NA, //INVOKESTATIC, // -
NA, //INVOKEINTERFACE, // -
NA, //UNUSED, // NOT VISITED
1, //NEW, // visitTypeInsn
0, //NEWARRAY, // visitIntInsn
0, //ANEWARRAY, // visitTypeInsn
0, //ARRAYLENGTH, // visitInsn
NA, //ATHROW, // -
0, //CHECKCAST, // visitTypeInsn
0, //INSTANCEOF, // -
-1, //MONITORENTER, // visitInsn
-1, //MONITOREXIT, // -
NA, //WIDE, // NOT VISITED
NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn
-1, //IFNULL, // visitJumpInsn
-1, //IFNONNULL, // -
NA, //GOTO_W, // -
NA, //JSR_W, // -
};
for (i = 0; i < b.length; ++i) {
System.err.print((char)('E' + b[i]));
}
System.err.println();
*/
if (cw.firstMethod == null) {
cw.firstMethod = this;
} else {
cw.lastMethod.next = this;
}
cw.lastMethod = this;
this.cw = cw;
this.computeMaxs = computeMaxs;
if (computeMaxs) {
// pushes the first block onto the stack of blocks to be visited
currentBlock = new Label();
currentBlock.pushed = true;
blockStack = currentBlock;
}
|
Methods Summary |
---|
private void | addSuccessor(int stackSize, oracle.toplink.libraries.asm.Label successor)Adds a successor to the {@link #currentBlock currentBlock} block.
Edge b;
// creates a new Edge object or reuses one from the shared pool
synchronized (SIZE) {
if (pool == null) {
b = new Edge();
} else {
b = pool;
// removes b from the pool
pool = pool.poolNext;
}
}
// adds the previous Edge to the list of Edges used by this CodeWriter
if (tail == null) {
tail = b;
}
b.poolNext = head;
head = b;
// initializes the previous Edge object...
b.stackSize = stackSize;
b.successor = successor;
// ...and adds it to the successor list of the currentBlock block
b.next = currentBlock.successors;
currentBlock.successors = b;
| private static int | getArgumentsAndReturnSizes(java.lang.String desc)Computes the size of the arguments and of the return value of a method.
int n = 1;
int c = 1;
while (true) {
char car = desc.charAt(c++);
if (car == ')") {
car = desc.charAt(c);
return n << 2 | (car == 'V" ? 0 : (car == 'D" || car == 'J" ? 2 : 1));
} else if (car == 'L") {
while (desc.charAt(c++) != ';") {
}
n += 1;
} else if (car == '[") {
while ((car = desc.charAt(c)) == '[") {
++c;
}
if (car == 'D" || car == 'J") {
n -= 1;
}
} else if (car == 'D" || car == 'J") {
n += 2;
} else {
n += 1;
}
}
| public byte[] | getCode()Returns the current bytecode of this method. This bytecode only contains
the instructions: it does not include the Exceptions, LocalVariableTable,
LineNumberTable, Synthetic and Deprecated attributes, if present.
return code.data;
| public int | getCodeSize()Returns the current size of the bytecode of this method. This size just
includes the size of the bytecode instructions: it does not include the
size of the Exceptions, LocalVariableTable, LineNumberTable, Synthetic
and Deprecated attributes, if present.
return code.length;
| static int | getNewOffset(int[] indexes, int[] sizes, int begin, int end)Computes the future value of a bytecode offset.
Note: it is possible to have several entries for the same instruction
in the indexes and sizes: two entries (index=a,size=b)
and (index=a,size=b') are equivalent to a single entry (index=a,size=b+b').
int offset = end - begin;
for (int i = 0; i < indexes.length; ++i) {
if (begin < indexes[i] && indexes[i] <= end) { // forward jump
offset += sizes[i];
} else if (end < indexes[i] && indexes[i] <= begin) { // backward jump
offset -= sizes[i];
}
}
return offset;
| final int | getSize()Returns the size of the bytecode of this method.
if (resize) {
// replaces the temporary jump opcodes introduced by Label.resolve.
resizeInstructions(new int[0], new int[0], 0);
}
int size = 8;
if (code.length > 0) {
cw.newUTF8("Code");
size += 18 + code.length + 8 * catchCount;
if (localVar != null) {
size += 8 + localVar.length;
}
if (lineNumber != null) {
size += 8 + lineNumber.length;
}
if (cattrs != null) {
size += cattrs.getSize(cw, code.data, code.length, maxStack, maxLocals);
}
}
if (exceptionCount > 0) {
cw.newUTF8("Exceptions");
size += 8 + 2 * exceptionCount;
}
if ((access & Constants.ACC_SYNTHETIC) != 0) {
cw.newUTF8("Synthetic");
size += 6;
}
if ((access & Constants.ACC_DEPRECATED) != 0) {
cw.newUTF8("Deprecated");
size += 6;
}
if (attrs != null) {
size += attrs.getSize(cw, null, 0, -1, -1);
}
return size;
| protected void | init(int access, java.lang.String name, java.lang.String desc, java.lang.String[] exceptions, oracle.toplink.libraries.asm.Attribute attrs)Initializes this CodeWriter to define the bytecode of the specified method.
this.access = access;
this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc);
if (exceptions != null && exceptions.length > 0) {
exceptionCount = exceptions.length;
this.exceptions = new int[exceptionCount];
for (int i = 0; i < exceptionCount; ++i) {
this.exceptions[i] = cw.newClass(exceptions[i]);
}
}
this.attrs = attrs;
if (computeMaxs) {
// updates maxLocals
int size = getArgumentsAndReturnSizes(desc) >> 2;
if ((access & Constants.ACC_STATIC) != 0) {
--size;
}
if (size > maxLocals) {
maxLocals = size;
}
}
| final void | put(oracle.toplink.libraries.asm.ByteVector out)Puts the bytecode of this method in the given byte vector.
out.putShort(access).putShort(name).putShort(desc);
int attributeCount = 0;
if (code.length > 0) {
++attributeCount;
}
if (exceptionCount > 0) {
++attributeCount;
}
if ((access & Constants.ACC_SYNTHETIC) != 0) {
++attributeCount;
}
if ((access & Constants.ACC_DEPRECATED) != 0) {
++attributeCount;
}
if (attrs != null) {
attributeCount += attrs.getCount();
}
out.putShort(attributeCount);
if (code.length > 0) {
int size = 12 + code.length + 8 * catchCount;
if (localVar != null) {
size += 8 + localVar.length;
}
if (lineNumber != null) {
size += 8 + lineNumber.length;
}
if (cattrs != null) {
size += cattrs.getSize(cw, code.data, code.length, maxStack, maxLocals);
}
out.putShort(cw.newUTF8("Code")).putInt(size);
out.putShort(maxStack).putShort(maxLocals);
out.putInt(code.length).putByteArray(code.data, 0, code.length);
out.putShort(catchCount);
if (catchCount > 0) {
out.putByteArray(catchTable.data, 0, catchTable.length);
}
attributeCount = 0;
if (localVar != null) {
++attributeCount;
}
if (lineNumber != null) {
++attributeCount;
}
if (cattrs != null) {
attributeCount += cattrs.getCount();
}
out.putShort(attributeCount);
if (localVar != null) {
out.putShort(cw.newUTF8("LocalVariableTable"));
out.putInt(localVar.length + 2).putShort(localVarCount);
out.putByteArray(localVar.data, 0, localVar.length);
}
if (lineNumber != null) {
out.putShort(cw.newUTF8("LineNumberTable"));
out.putInt(lineNumber.length + 2).putShort(lineNumberCount);
out.putByteArray(lineNumber.data, 0, lineNumber.length);
}
if (cattrs != null) {
cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);
}
}
if (exceptionCount > 0) {
out.putShort(cw.newUTF8("Exceptions")).putInt(2 * exceptionCount + 2);
out.putShort(exceptionCount);
for (int i = 0; i < exceptionCount; ++i) {
out.putShort(exceptions[i]);
}
}
if ((access & Constants.ACC_SYNTHETIC) != 0) {
out.putShort(cw.newUTF8("Synthetic")).putInt(0);
}
if ((access & Constants.ACC_DEPRECATED) != 0) {
out.putShort(cw.newUTF8("Deprecated")).putInt(0);
}
if (attrs != null) {
attrs.put(cw, null, 0, -1, -1, out);
}
| static int | readInt(byte[] b, int index)Reads a signed int value in the given byte array.
return ((b[index] & 0xFF) << 24) |
((b[index + 1] & 0xFF) << 16) |
((b[index + 2] & 0xFF) << 8) |
(b[index + 3] & 0xFF);
| static short | readShort(byte[] b, int index)Reads a signed short value in the given byte array.
return (short)(((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
| static int | readUnsignedShort(byte[] b, int index)Reads an unsigned short value in the given byte array.
return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
| protected int[] | resizeInstructions(int[] indexes, int[] sizes, int len)Resizes the designated instructions, while keeping jump offsets and
instruction addresses consistent. This may require to resize other existing
instructions, or even to introduce new instructions: for example,
increasing the size of an instruction by 2 at the middle of a method can
increases the offset of an IFEQ instruction from 32766 to 32768, in which
case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W 32765. This, in turn,
may require to increase the size of another jump instruction, and so on...
All these operations are handled automatically by this method.
This method must be called after all the method that is being built has
been visited. In particular, the {@link Label Label} objects used to
construct the method are no longer valid after this method has been called.
byte[] b = code.data; // bytecode of the method
int u, v, label; // indexes in b
int i, j; // loop indexes
// 1st step:
// As explained above, resizing an instruction may require to resize another
// one, which may require to resize yet another one, and so on. The first
// step of the algorithm consists in finding all the instructions that
// need to be resized, without modifying the code. This is done by the
// following "fix point" algorithm:
// - parse the code to find the jump instructions whose offset will need
// more than 2 bytes to be stored (the future offset is computed from the
// current offset and from the number of bytes that will be inserted or
// removed between the source and target instructions). For each such
// instruction, adds an entry in (a copy of) the indexes and sizes arrays
// (if this has not already been done in a previous iteration!)
// - if at least one entry has been added during the previous step, go back
// to the beginning, otherwise stop.
// In fact the real algorithm is complicated by the fact that the size of
// TABLESWITCH and LOOKUPSWITCH instructions depends on their position in
// the bytecode (because of padding). In order to ensure the convergence of
// the algorithm, the number of bytes to be added or removed from these
// instructions is over estimated during the previous loop, and computed
// exactly only after the loop is finished (this requires another pass to
// parse the bytecode of the method).
int[] allIndexes = new int[len]; // copy of indexes
int[] allSizes = new int[len]; // copy of sizes
boolean[] resize; // instructions to be resized
int newOffset; // future offset of a jump instruction
System.arraycopy(indexes, 0, allIndexes, 0, len);
System.arraycopy(sizes, 0, allSizes, 0, len);
resize = new boolean[code.length];
int state = 3; // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
do {
if (state == 3) {
state = 2;
}
u = 0;
while (u < b.length) {
int opcode = b[u] & 0xFF; // opcode of current instruction
int insert = 0; // bytes to be added after this instruction
switch (ClassWriter.TYPE[opcode]) {
case ClassWriter.NOARG_INSN:
case ClassWriter.IMPLVAR_INSN:
u += 1;
break;
case ClassWriter.LABEL_INSN:
if (opcode > 201) {
// converts temporary opcodes 202 to 217 (inclusive), 218 and 219
// to IFEQ ... JSR (inclusive), IFNULL and IFNONNULL
opcode = opcode < 218 ? opcode - 49 : opcode - 20;
label = u + readUnsignedShort(b, u + 1);
} else {
label = u + readShort(b, u + 1);
}
newOffset = getNewOffset(allIndexes, allSizes, u, label);
if (newOffset < Short.MIN_VALUE || newOffset > Short.MAX_VALUE) {
if (!resize[u]) {
if (opcode == Constants.GOTO || opcode == Constants.JSR) {
// two additional bytes will be required to replace this
// GOTO or JSR instruction with a GOTO_W or a JSR_W
insert = 2;
} else {
// five additional bytes will be required to replace this
// IFxxx <l> instruction with IFNOTxxx <l'> GOTO_W <l>, where
// IFNOTxxx is the "opposite" opcode of IFxxx (i.e., IFNE for
// IFEQ) and where <l'> designates the instruction just after
// the GOTO_W.
insert = 5;
}
resize[u] = true;
}
}
u += 3;
break;
case ClassWriter.LABELW_INSN:
u += 5;
break;
case ClassWriter.TABL_INSN:
if (state == 1) {
// true number of bytes to be added (or removed) from this
// instruction = (future number of padding bytes - current number
// of padding byte) - previously over estimated variation =
// = ((3 - newOffset%4) - (3 - u%4)) - u%4
// = (-newOffset%4 + u%4) - u%4
// = -(newOffset & 3)
newOffset = getNewOffset(allIndexes, allSizes, 0, u);
insert = -(newOffset & 3);
} else if (!resize[u]) {
// over estimation of the number of bytes to be added to this
// instruction = 3 - current number of padding bytes = 3 - (3 -
// u%4) = u%4 = u & 3
insert = u & 3;
resize[u] = true;
}
// skips instruction
u = u + 4 - (u & 3);
u += 4*(readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
break;
case ClassWriter.LOOK_INSN:
if (state == 1) {
// like TABL_INSN
newOffset = getNewOffset(allIndexes, allSizes, 0, u);
insert = -(newOffset & 3);
} else if (!resize[u]) {
// like TABL_INSN
insert = u & 3;
resize[u] = true;
}
// skips instruction
u = u + 4 - (u & 3);
u += 8*readInt(b, u + 4) + 8;
break;
case ClassWriter.WIDE_INSN:
opcode = b[u + 1] & 0xFF;
if (opcode == Constants.IINC) {
u += 6;
} else {
u += 4;
}
break;
case ClassWriter.VAR_INSN:
case ClassWriter.SBYTE_INSN:
case ClassWriter.LDC_INSN:
u += 2;
break;
case ClassWriter.SHORT_INSN:
case ClassWriter.LDCW_INSN:
case ClassWriter.FIELDORMETH_INSN:
case ClassWriter.TYPE_INSN:
case ClassWriter.IINC_INSN:
u += 3;
break;
case ClassWriter.ITFMETH_INSN:
u += 5;
break;
// case ClassWriter.MANA_INSN:
default:
u += 4;
break;
}
if (insert != 0) {
// adds a new (u, insert) entry in the allIndexes and allSizes arrays
int[] newIndexes = new int[allIndexes.length + 1];
int[] newSizes = new int[allSizes.length + 1];
System.arraycopy(allIndexes, 0, newIndexes, 0, allIndexes.length);
System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length);
newIndexes[allIndexes.length] = u;
newSizes[allSizes.length] = insert;
allIndexes = newIndexes;
allSizes = newSizes;
if (insert > 0) {
state = 3;
}
}
}
if (state < 3) {
--state;
}
} while (state != 0);
// 2nd step:
// copies the bytecode of the method into a new bytevector, updates the
// offsets, and inserts (or removes) bytes as requested.
ByteVector newCode = new ByteVector(code.length);
u = 0;
while (u < code.length) {
for (i = allIndexes.length - 1; i >= 0; --i) {
if (allIndexes[i] == u) {
if (i < len) {
if (sizes[i] > 0) {
newCode.putByteArray(null, 0, sizes[i]);
} else {
newCode.length += sizes[i];
}
indexes[i] = newCode.length;
}
}
}
int opcode = b[u] & 0xFF;
switch (ClassWriter.TYPE[opcode]) {
case ClassWriter.NOARG_INSN:
case ClassWriter.IMPLVAR_INSN:
newCode.putByte(opcode);
u += 1;
break;
case ClassWriter.LABEL_INSN:
if (opcode > 201) {
// changes temporary opcodes 202 to 217 (inclusive), 218 and 219
// to IFEQ ... JSR (inclusive), IFNULL and IFNONNULL
opcode = opcode < 218 ? opcode - 49 : opcode - 20;
label = u + readUnsignedShort(b, u + 1);
} else {
label = u + readShort(b, u + 1);
}
newOffset = getNewOffset(allIndexes, allSizes, u, label);
if (resize[u]) {
// replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx <l> with
// IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the "opposite" opcode
// of IFxxx (i.e., IFNE for IFEQ) and where <l'> designates the
// instruction just after the GOTO_W.
if (opcode == Constants.GOTO) {
newCode.putByte(200); // GOTO_W
} else if (opcode == Constants.JSR) {
newCode.putByte(201); // JSR_W
} else {
newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1);
newCode.putShort(8); // jump offset
newCode.putByte(200); // GOTO_W
newOffset -= 3; // newOffset now computed from start of GOTO_W
}
newCode.putInt(newOffset);
} else {
newCode.putByte(opcode);
newCode.putShort(newOffset);
}
u += 3;
break;
case ClassWriter.LABELW_INSN:
label = u + readInt(b, u + 1);
newOffset = getNewOffset(allIndexes, allSizes, u, label);
newCode.putByte(opcode);
newCode.putInt(newOffset);
u += 5;
break;
case ClassWriter.TABL_INSN:
// skips 0 to 3 padding bytes
v = u;
u = u + 4 - (v & 3);
// reads and copies instruction
newCode.putByte(Constants.TABLESWITCH);
while (newCode.length % 4 != 0) {
newCode.putByte(0);
}
label = v + readInt(b, u); u += 4;
newOffset = getNewOffset(allIndexes, allSizes, v, label);
newCode.putInt(newOffset);
j = readInt(b, u); u += 4;
newCode.putInt(j);
j = readInt(b, u) - j + 1; u += 4;
newCode.putInt(readInt(b, u - 4));
for ( ; j > 0; --j) {
label = v + readInt(b, u); u += 4;
newOffset = getNewOffset(allIndexes, allSizes, v, label);
newCode.putInt(newOffset);
}
break;
case ClassWriter.LOOK_INSN:
// skips 0 to 3 padding bytes
v = u;
u = u + 4 - (v & 3);
// reads and copies instruction
newCode.putByte(Constants.LOOKUPSWITCH);
while (newCode.length % 4 != 0) {
newCode.putByte(0);
}
label = v + readInt(b, u); u += 4;
newOffset = getNewOffset(allIndexes, allSizes, v, label);
newCode.putInt(newOffset);
j = readInt(b, u); u += 4;
newCode.putInt(j);
for ( ; j > 0; --j) {
newCode.putInt(readInt(b, u)); u += 4;
label = v + readInt(b, u); u += 4;
newOffset = getNewOffset(allIndexes, allSizes, v, label);
newCode.putInt(newOffset);
}
break;
case ClassWriter.WIDE_INSN:
opcode = b[u + 1] & 0xFF;
if (opcode == Constants.IINC) {
newCode.putByteArray(b, u, 6);
u += 6;
} else {
newCode.putByteArray(b, u, 4);
u += 4;
}
break;
case ClassWriter.VAR_INSN:
case ClassWriter.SBYTE_INSN:
case ClassWriter.LDC_INSN:
newCode.putByteArray(b, u, 2);
u += 2;
break;
case ClassWriter.SHORT_INSN:
case ClassWriter.LDCW_INSN:
case ClassWriter.FIELDORMETH_INSN:
case ClassWriter.TYPE_INSN:
case ClassWriter.IINC_INSN:
newCode.putByteArray(b, u, 3);
u += 3;
break;
case ClassWriter.ITFMETH_INSN:
newCode.putByteArray(b, u, 5);
u += 5;
break;
// case MANA_INSN:
default:
newCode.putByteArray(b, u, 4);
u += 4;
break;
}
}
// updates the instructions addresses in the
// catch, local var and line number tables
if (catchTable != null) {
b = catchTable.data;
u = 0;
while (u < catchTable.length) {
writeShort(b, u, getNewOffset(
allIndexes, allSizes, 0, readUnsignedShort(b, u)));
writeShort(b, u + 2, getNewOffset(
allIndexes, allSizes, 0, readUnsignedShort(b, u + 2)));
writeShort(b, u + 4, getNewOffset(
allIndexes, allSizes, 0, readUnsignedShort(b, u + 4)));
u += 8;
}
}
if (localVar != null) {
b = localVar.data;
u = 0;
while (u < localVar.length) {
label = readUnsignedShort(b, u);
newOffset = getNewOffset(allIndexes, allSizes, 0, label);
writeShort(b, u, newOffset);
label += readUnsignedShort(b, u + 2);
newOffset = getNewOffset(allIndexes, allSizes, 0, label) - newOffset;
writeShort(b, u + 2, newOffset);
u += 10;
}
}
if (lineNumber != null) {
b = lineNumber.data;
u = 0;
while (u < lineNumber.length) {
writeShort(b, u, getNewOffset(
allIndexes, allSizes, 0, readUnsignedShort(b, u)));
u += 4;
}
}
// updates the labels of the other attributes
while (cattrs != null) {
Label[] labels = cattrs.getLabels();
if (labels != null) {
for (i = labels.length - 1; i >= 0; --i) {
if (!labels[i].resized) {
labels[i].position =
getNewOffset(allIndexes, allSizes, 0, labels[i].position);
labels[i].resized = true;
}
}
}
}
// replaces old bytecodes with new ones
code = newCode;
// returns the positions of the resized instructions
return indexes;
| public void | visitAttribute(oracle.toplink.libraries.asm.Attribute attr)
attr.next = cattrs;
cattrs = attr;
| public void | visitFieldInsn(int opcode, java.lang.String owner, java.lang.String name, java.lang.String desc)
if (computeMaxs) {
int size;
// computes the stack size variation
char c = desc.charAt(0);
switch (opcode) {
case Constants.GETSTATIC:
size = stackSize + (c == 'D" || c == 'J" ? 2 : 1);
break;
case Constants.PUTSTATIC:
size = stackSize + (c == 'D" || c == 'J" ? -2 : -1);
break;
case Constants.GETFIELD:
size = stackSize + (c == 'D" || c == 'J" ? 1 : 0);
break;
//case Constants.PUTFIELD:
default:
size = stackSize + (c == 'D" || c == 'J" ? -3 : -2);
break;
}
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
// adds the instruction to the bytecode of the method
code.put12(opcode, cw.newField(owner, name, desc));
| public void | visitIincInsn(int var, int increment)
if (computeMaxs) {
// updates max locals only (no stack change)
int n = var + 1;
if (n > maxLocals) {
maxLocals = n;
}
}
// adds the instruction to the bytecode of the method
if ((var > 255) || (increment > 127) || (increment < -128)) {
code.putByte(196 /*WIDE*/).put12(Constants.IINC, var).putShort(increment);
} else {
code.putByte(Constants.IINC).put11(var, increment);
}
| public void | visitInsn(int opcode)
if (computeMaxs) {
// updates current and max stack sizes
int size = stackSize + SIZE[opcode];
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
// if opcode == ATHROW or xRETURN, ends current block (no successor)
if ((opcode >= Constants.IRETURN && opcode <= Constants.RETURN) ||
opcode == Constants.ATHROW)
{
if (currentBlock != null) {
currentBlock.maxStackSize = maxStackSize;
currentBlock = null;
}
}
}
// adds the instruction to the bytecode of the method
code.putByte(opcode);
| public void | visitIntInsn(int opcode, int operand)
if (computeMaxs && opcode != Constants.NEWARRAY) {
// updates current and max stack sizes only if opcode == NEWARRAY
// (stack size variation = 0 for BIPUSH or SIPUSH)
int size = stackSize + 1;
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
// adds the instruction to the bytecode of the method
if (opcode == Constants.SIPUSH) {
code.put12(opcode, operand);
} else { // BIPUSH or NEWARRAY
code.put11(opcode, operand);
}
| public void | visitJumpInsn(int opcode, oracle.toplink.libraries.asm.Label label)
if (CHECK) {
if (label.owner == null) {
label.owner = this;
} else if (label.owner != this) {
throw new IllegalArgumentException();
}
}
if (computeMaxs) {
if (opcode == Constants.GOTO) {
// no stack change, but end of current block (with one new successor)
if (currentBlock != null) {
currentBlock.maxStackSize = maxStackSize;
addSuccessor(stackSize, label);
currentBlock = null;
}
} else if (opcode == Constants.JSR) {
if (currentBlock != null) {
addSuccessor(stackSize + 1, label);
}
} else {
// updates current stack size (max stack size unchanged because stack
// size variation always negative in this case)
stackSize += SIZE[opcode];
if (currentBlock != null) {
addSuccessor(stackSize, label);
}
}
}
// adds the instruction to the bytecode of the method
if (label.resolved && label.position - code.length < Short.MIN_VALUE) {
// case of a backward jump with an offset < -32768. In this case we
// automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx <l>
// with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the "opposite" opcode
// of IFxxx (i.e., IFNE for IFEQ) and where <l'> designates the
// instruction just after the GOTO_W.
if (opcode == Constants.GOTO) {
code.putByte(200); // GOTO_W
} else if (opcode == Constants.JSR) {
code.putByte(201); // JSR_W
} else {
code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1);
code.putShort(8); // jump offset
code.putByte(200); // GOTO_W
}
label.put(this, code, code.length - 1, true);
} else {
// case of a backward jump with an offset >= -32768, or of a forward jump
// with, of course, an unknown offset. In these cases we store the offset
// in 2 bytes (which will be increased in resizeInstructions, if needed).
code.putByte(opcode);
label.put(this, code, code.length - 1, false);
}
| public void | visitLabel(oracle.toplink.libraries.asm.Label label)
if (CHECK) {
if (label.owner == null) {
label.owner = this;
} else if (label.owner != this) {
throw new IllegalArgumentException();
}
}
if (computeMaxs) {
if (currentBlock != null) {
// ends current block (with one new successor)
currentBlock.maxStackSize = maxStackSize;
addSuccessor(stackSize, label);
}
// begins a new current block,
// resets the relative current and max stack sizes
currentBlock = label;
stackSize = 0;
maxStackSize = 0;
}
// resolves previous forward references to label, if any
resize |= label.resolve(this, code.length, code.data);
| public void | visitLdcInsn(java.lang.Object cst)
Item i = cw.newConstItem(cst);
if (computeMaxs) {
int size;
// computes the stack size variation
if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
size = stackSize + 2;
} else {
size = stackSize + 1;
}
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
// adds the instruction to the bytecode of the method
int index = i.index;
if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
code.put12(20 /*LDC2_W*/, index);
} else if (index >= 256) {
code.put12(19 /*LDC_W*/, index);
} else {
code.put11(Constants.LDC, index);
}
| public void | visitLineNumber(int line, oracle.toplink.libraries.asm.Label start)
if (CHECK) {
if (start.owner != this || !start.resolved) {
throw new IllegalArgumentException();
}
}
if (lineNumber == null) {
cw.newUTF8("LineNumberTable");
lineNumber = new ByteVector();
}
++lineNumberCount;
lineNumber.putShort(start.position);
lineNumber.putShort(line);
| public void | visitLocalVariable(java.lang.String name, java.lang.String desc, oracle.toplink.libraries.asm.Label start, oracle.toplink.libraries.asm.Label end, int index)
if (CHECK) {
if (start.owner != this || !start.resolved) {
throw new IllegalArgumentException();
}
if (end.owner != this || !end.resolved) {
throw new IllegalArgumentException();
}
}
if (localVar == null) {
cw.newUTF8("LocalVariableTable");
localVar = new ByteVector();
}
++localVarCount;
localVar.putShort(start.position);
localVar.putShort(end.position - start.position);
localVar.putShort(cw.newUTF8(name));
localVar.putShort(cw.newUTF8(desc));
localVar.putShort(index);
| public void | visitLookupSwitchInsn(oracle.toplink.libraries.asm.Label dflt, int[] keys, oracle.toplink.libraries.asm.Label[] labels)
if (computeMaxs) {
// updates current stack size (max stack size unchanged)
--stackSize;
// ends current block (with many new successors)
if (currentBlock != null) {
currentBlock.maxStackSize = maxStackSize;
addSuccessor(stackSize, dflt);
for (int i = 0; i < labels.length; ++i) {
addSuccessor(stackSize, labels[i]);
}
currentBlock = null;
}
}
// adds the instruction to the bytecode of the method
int source = code.length;
code.putByte(Constants.LOOKUPSWITCH);
while (code.length % 4 != 0) {
code.putByte(0);
}
dflt.put(this, code, source, true);
code.putInt(labels.length);
for (int i = 0; i < labels.length; ++i) {
code.putInt(keys[i]);
labels[i].put(this, code, source, true);
}
| public void | visitMaxs(int maxStack, int maxLocals)
if (computeMaxs) {
// true (non relative) max stack size
int max = 0;
// control flow analysis algorithm: while the block stack is not empty,
// pop a block from this stack, update the max stack size, compute
// the true (non relative) begin stack size of the successors of this
// block, and push these successors onto the stack (unless they have
// already been pushed onto the stack). Note: by hypothesis, the {@link
// Label#beginStackSize} of the blocks in the block stack are the true
// (non relative) beginning stack sizes of these blocks.
Label stack = blockStack;
while (stack != null) {
// pops a block from the stack
Label l = stack;
stack = stack.next;
// computes the true (non relative) max stack size of this block
int start = l.beginStackSize;
int blockMax = start + l.maxStackSize;
// updates the global max stack size
if (blockMax > max) {
max = blockMax;
}
// analyses the successors of the block
Edge b = l.successors;
while (b != null) {
l = b.successor;
// if this successor has not already been pushed onto the stack...
if (!l.pushed) {
// computes the true beginning stack size of this successor block
l.beginStackSize = start + b.stackSize;
// pushes this successor onto the stack
l.pushed = true;
l.next = stack;
stack = l;
}
b = b.next;
}
}
this.maxStack = max;
// releases all the Edge objects used by this CodeWriter
synchronized (SIZE) {
// appends the [head ... tail] list at the beginning of the pool list
if (tail != null) {
tail.poolNext = pool;
pool = head;
}
}
} else {
this.maxStack = maxStack;
this.maxLocals = maxLocals;
}
| public void | visitMethodInsn(int opcode, java.lang.String owner, java.lang.String name, java.lang.String desc)
boolean itf = opcode == Constants.INVOKEINTERFACE;
Item i = cw.newMethodItem(owner, name, desc, itf);
int argSize = i.intVal;
if (computeMaxs) {
// computes the stack size variation. In order not to recompute several
// times this variation for the same Item, we use the intVal field of
// this item to store this variation, once it has been computed. More
// precisely this intVal field stores the sizes of the arguments and of
// the return value corresponding to desc.
if (argSize == 0) {
// the above sizes have not been computed yet, so we compute them...
argSize = getArgumentsAndReturnSizes(desc);
// ... and we save them in order not to recompute them in the future
i.intVal = argSize;
}
int size;
if (opcode == Constants.INVOKESTATIC) {
size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
} else {
size = stackSize - (argSize >> 2) + (argSize & 0x03);
}
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
// adds the instruction to the bytecode of the method
if (itf) {
if (!computeMaxs) {
if (argSize == 0) {
argSize = getArgumentsAndReturnSizes(desc);
i.intVal = argSize;
}
}
code.put12(Constants.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
} else {
code.put12(opcode, i.index);
}
| public void | visitMultiANewArrayInsn(java.lang.String desc, int dims)
if (computeMaxs) {
// updates current stack size (max stack size unchanged because stack
// size variation always negative or null)
stackSize += 1 - dims;
}
// adds the instruction to the bytecode of the method
code.put12(Constants.MULTIANEWARRAY, cw.newClass(desc)).putByte(dims);
| public void | visitTableSwitchInsn(int min, int max, oracle.toplink.libraries.asm.Label dflt, oracle.toplink.libraries.asm.Label[] labels)
if (computeMaxs) {
// updates current stack size (max stack size unchanged)
--stackSize;
// ends current block (with many new successors)
if (currentBlock != null) {
currentBlock.maxStackSize = maxStackSize;
addSuccessor(stackSize, dflt);
for (int i = 0; i < labels.length; ++i) {
addSuccessor(stackSize, labels[i]);
}
currentBlock = null;
}
}
// adds the instruction to the bytecode of the method
int source = code.length;
code.putByte(Constants.TABLESWITCH);
while (code.length % 4 != 0) {
code.putByte(0);
}
dflt.put(this, code, source, true);
code.putInt(min).putInt(max);
for (int i = 0; i < labels.length; ++i) {
labels[i].put(this, code, source, true);
}
| public void | visitTryCatchBlock(oracle.toplink.libraries.asm.Label start, oracle.toplink.libraries.asm.Label end, oracle.toplink.libraries.asm.Label handler, java.lang.String type)
if (CHECK) {
if (start.owner != this || end.owner != this || handler.owner != this) {
throw new IllegalArgumentException();
}
if (!start.resolved || !end.resolved || !handler.resolved) {
throw new IllegalArgumentException();
}
}
if (computeMaxs) {
// pushes handler block onto the stack of blocks to be visited
if (!handler.pushed) {
handler.beginStackSize = 1;
handler.pushed = true;
handler.next = blockStack;
blockStack = handler;
}
}
++catchCount;
if (catchTable == null) {
catchTable = new ByteVector();
}
catchTable.putShort(start.position);
catchTable.putShort(end.position);
catchTable.putShort(handler.position);
catchTable.putShort(type != null ? cw.newClass(type) : 0);
| public void | visitTypeInsn(int opcode, java.lang.String desc)
if (computeMaxs && opcode == Constants.NEW) {
// updates current and max stack sizes only if opcode == NEW
// (stack size variation = 0 for ANEWARRAY, CHECKCAST, INSTANCEOF)
int size = stackSize + 1;
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
// adds the instruction to the bytecode of the method
code.put12(opcode, cw.newClass(desc));
| public void | visitVarInsn(int opcode, int var)
if (computeMaxs) {
// updates current and max stack sizes
if (opcode == Constants.RET) {
// no stack change, but end of current block (no successor)
if (currentBlock != null) {
currentBlock.maxStackSize = maxStackSize;
currentBlock = null;
}
} else { // xLOAD or xSTORE
int size = stackSize + SIZE[opcode];
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
// updates max locals
int n;
if (opcode == Constants.LLOAD || opcode == Constants.DLOAD ||
opcode == Constants.LSTORE || opcode == Constants.DSTORE)
{
n = var + 2;
} else {
n = var + 1;
}
if (n > maxLocals) {
maxLocals = n;
}
}
// adds the instruction to the bytecode of the method
if (var < 4 && opcode != Constants.RET) {
int opt;
if (opcode < Constants.ISTORE) {
opt = 26 /*ILOAD_0*/ + ((opcode - Constants.ILOAD) << 2) + var;
} else {
opt = 59 /*ISTORE_0*/ + ((opcode - Constants.ISTORE) << 2) + var;
}
code.putByte(opt);
} else if (var >= 256) {
code.putByte(196 /*WIDE*/).put12(opcode, var);
} else {
code.put11(opcode, var);
}
| static void | writeShort(byte[] b, int index, int s)Writes a short value in the given byte array.
b[index] = (byte)(s >>> 8);
b[index + 1] = (byte)s;
|
|