📄 codewriter.java
字号:
* <tt>i & 0x03</tt>).
*/
private static int getArgumentsAndReturnSizes (final String desc) {
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;
}
}
}
/**
* Adds a successor to the {@link #currentBlock currentBlock} block.
*
* @param stackSize the current (relative) stack size in the current block.
* @param successor the successor block to be added to the current block.
*/
private void addSuccessor (final int stackSize, final Label successor) {
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;
}
// --------------------------------------------------------------------------
// Utility methods: dump bytecode array
// --------------------------------------------------------------------------
/**
* Returns the size of the bytecode of this method.
*
* @return the size of the bytecode of this method.
*/
final int getSize () {
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;
}
/**
* Puts the bytecode of this method in the given byte vector.
*
* @param out the byte vector into which the bytecode of this method must be
* copied.
*/
final void put (final ByteVector out) {
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);
}
}
// --------------------------------------------------------------------------
// Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
// --------------------------------------------------------------------------
/**
* 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.
* <p>
* <i>This method must be called after all the method that is being built has
* been visited</i>. In particular, the {@link Label Label} objects used to
* construct the method are no longer valid after this method has been called.
*
* @param indexes current positions of the instructions to be resized. Each
* instruction must be designated by the index of its <i>last</i> byte,
* plus one (or, in other words, by the index of the <i>first</i> byte of
* the <i>next</i> instruction).
* @param sizes the number of bytes to be <i>added</i> to the above
* instructions. More precisely, for each i < <tt>len</tt>,
* <tt>sizes</tt>[i] bytes will be added at the end of the instruction
* designated by <tt>indexes</tt>[i] or, if <tt>sizes</tt>[i] is
* negative, the <i>last</i> |<tt>sizes[i]</tt>| bytes of the instruction
* will be removed (the instruction size <i>must not</i> become negative
* or null). The gaps introduced by this method must be filled in
* "manually" in the array returned by the {@link #getCode getCode}
* method.
* @param len the number of instruction to be resized. Must be smaller than or
* equal to <tt>indexes</tt>.length and <tt>sizes</tt>.length.
* @return the <tt>indexes</tt> array, which now contains the new positions of
* the resized instructions (designated as above).
*/
protected int[] resizeInstructions (
final int[] indexes,
final int[] sizes,
final int len)
{
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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -