package javassist.bytecode;
+import java.util.ArrayList;
+
/**
* An iterator for editing a code attribute.
*
protected byte[] bytecode;
protected int endPos;
protected int currentPos;
+ protected int mark;
protected CodeIterator(CodeAttribute ca) {
codeAttr = ca;
* Moves to the first instruction.
*/
public void begin() {
- currentPos = 0;
+ currentPos = mark = 0;
endPos = getCodeLength();
}
currentPos = index;
}
+ /**
+ * Sets a mark to the bytecode at the given index.
+ * The mark can be used to track the position of that bytecode
+ * when code blocks are inserted.
+ * If a code block is inclusively inserted at the position of the
+ * bytecode, the mark is set to the inserted code block.
+ *
+ * @see #getMark()
+ * @since 3.11
+ */
+ public void setMark(int index) {
+ mark = index;
+ }
+
+ /**
+ * Gets the index of the position of the mark set by
+ * <code>setMark</code>.
+ *
+ * @return the index of the position.
+ * @see #setMark(int)
+ * @since 3.11
+ */
+ public int getMark() { return mark; }
+
/**
* Returns a Code attribute read with this iterator.
*/
* Inserts the given bytecode sequence
* before the next instruction that would be returned by
* <code>next()</code> (not before the instruction returned
- * by tha last call to <code>next()</code>).
+ * by the last call to <code>next()</code>).
* Branch offsets and the exception table are also updated.
*
* <p>If the next instruction is at the beginning of a block statement,
public int insert(byte[] code)
throws BadBytecode
{
- int pos = currentPos;
- insert0(currentPos, code, false);
- return pos;
+ return insert0(currentPos, code, false);
}
/**
* bytecode sequence for adjusting alignment if the code attribute
* includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
*
+ * <p>The index at which the byte sequence is actually inserted
+ * might be different from pos since some other bytes might be
+ * inserted at other positions (e.g. to change <code>GOTO</code>
+ * to <code>GOTO_W</code>).
+ *
* @param pos the index at which a byte sequence is inserted.
* @param code inserted bytecode sequence.
*/
insert0(pos, code, false);
}
+ /**
+ * Inserts the given bytecode sequence
+ * before the instruction at the given index <code>pos</code>.
+ * Branch offsets and the exception table are also updated.
+ *
+ * <p>If the instruction at the given index is at the beginning
+ * of a block statement,
+ * then the bytecode is inserted within that block.
+ *
+ * <p>An extra gap may be inserted at the end of the inserted
+ * bytecode sequence for adjusting alignment if the code attribute
+ * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
+ *
+ * @param pos the index at which a byte sequence is inserted.
+ * @param code inserted bytecode sequence.
+ * @return the index indicating the first byte of the
+ * inserted byte sequence, which might be
+ * different from pos.
+ * @since 3.11
+ */
+ public int insertAt(int pos, byte[] code) throws BadBytecode {
+ return insert0(pos, code, false);
+ }
+
/**
* Inserts the given bytecode sequence exclusively
* before the next instruction that would be returned by
public int insertEx(byte[] code)
throws BadBytecode
{
- int pos = currentPos;
- insert0(currentPos, code, true);
- return pos;
+ return insert0(currentPos, code, true);
}
/**
* bytecode sequence for adjusting alignment if the code attribute
* includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
*
+ * <p>The index at which the byte sequence is actually inserted
+ * might be different from pos since some other bytes might be
+ * inserted at other positions (e.g. to change <code>GOTO</code>
+ * to <code>GOTO_W</code>).
+ *
* @param pos the index at which a byte sequence is inserted.
* @param code inserted bytecode sequence.
*/
insert0(pos, code, true);
}
- private void insert0(int pos, byte[] code, boolean exclusive)
+ /**
+ * Inserts the given bytecode sequence exclusively
+ * before the instruction at the given index <code>pos</code>.
+ * Branch offsets and the exception table are also updated.
+ *
+ * <p>If the instruction at the given index is at the beginning
+ * of a block statement,
+ * then the bytecode is excluded from that block.
+ *
+ * <p>An extra gap may be inserted at the end of the inserted
+ * bytecode sequence for adjusting alignment if the code attribute
+ * includes <code>LOOKUPSWITCH</code> or <code>TABLESWITCH</code>.
+ *
+ * @param pos the index at which a byte sequence is inserted.
+ * @param code inserted bytecode sequence.
+ * @return the index indicating the first byte of the
+ * inserted byte sequence, which might be
+ * different from pos.
+ * @since 3.11
+ */
+ public int insertExAt(int pos, byte[] code) throws BadBytecode {
+ return insert0(pos, code, true);
+ }
+
+ /**
+ * @return the index indicating the first byte of the
+ * inserted byte sequence.
+ */
+ private int insert0(int pos, byte[] code, boolean exclusive)
throws BadBytecode
{
int len = code.length;
if (len <= 0)
- return;
+ return pos;
+
+ // currentPos will change.
+ pos = insertGapAt(pos, len, exclusive).position;
- insertGapCore(pos, len, exclusive); // currentPos will change.
+ int p = pos;
for (int j = 0; j < len; ++j)
- bytecode[pos++] = code[j];
+ bytecode[p++] = code[j];
+
+ return pos;
}
/**
* @return the index indicating the first byte of the inserted gap.
*/
public int insertGap(int length) throws BadBytecode {
- int pos = currentPos;
- insertGapCore(currentPos, length, false);
- return pos;
+ return insertGapAt(currentPos, length, false).position;
}
/**
* It might be bigger than <code>length</code>.
*/
public int insertGap(int pos, int length) throws BadBytecode {
- return insertGapCore(pos, length, false);
+ return insertGapAt(pos, length, false).length;
}
/**
* Inserts an exclusive gap
* before the next instruction that would be returned by
* <code>next()</code> (not before the instruction returned
- * by tha last call to <code>next()</code>).
+ * by the last call to <code>next()</code>).
* Branch offsets and the exception table are also updated.
* The inserted gap is filled with NOP. The gap length may be
* extended to a multiple of 4.
* @return the index indicating the first byte of the inserted gap.
*/
public int insertExGap(int length) throws BadBytecode {
- int pos = currentPos;
- insertGapCore(currentPos, length, true);
- return pos;
+ return insertGapAt(currentPos, length, true).position;
}
/**
* It might be bigger than <code>length</code>.
*/
public int insertExGap(int pos, int length) throws BadBytecode {
- return insertGapCore(pos, length, true);
+ return insertGapAt(pos, length, true).length;
}
/**
- * @return the length of the really inserted gap.
+ * An inserted gap.
+ *
+ * @since 3.11
*/
- private int insertGapCore(int pos, int length, boolean exclusive)
+ public static class Gap {
+ /**
+ * The position of the gap.
+ */
+ public int position;
+
+ /**
+ * The length of the gap.
+ */
+ public int length;
+ }
+
+ /**
+ * Inserts an inclusive or exclusive gap in front of the instruction
+ * at the given index <code>pos</code>.
+ * Branch offsets and the exception table in the method body
+ * are also updated. The inserted gap is filled with NOP.
+ * The gap length may be extended to a multiple of 4.
+ *
+ * <p>Suppose that the instruction at the given index is at the
+ * beginning of a block statement. If the gap is inclusive,
+ * then it is included within that block. If the gap is exclusive,
+ * then it is excluded from that block.
+ *
+ * <p>The index at which the gap is actually inserted
+ * might be different from pos since some other bytes might be
+ * inserted at other positions (e.g. to change <code>GOTO</code>
+ * to <code>GOTO_W</code>). The index is available from the <code>Gap</code>
+ * object returned by this method.
+ *
+ * <p>Suppose that the gap is inserted at the position of
+ * the next instruction that would be returned by
+ * <code>next()</code> (not the last instruction returned
+ * by the last call to <code>next()</code>). The next
+ * instruction returned by <code>next()</code> after the gap is
+ * inserted is still the same instruction. It is not <code>NOP</code>
+ * at the first byte of the inserted gap.
+ *
+ * @param pos the index at which a gap is inserted.
+ * @param length gap length.
+ * @param exclusive true if exclusive, otherwise false.
+ * @return the position and the length of the inserted gap.
+ * @since 3.11
+ */
+ public Gap insertGapAt(int pos, int length, boolean exclusive)
throws BadBytecode
{
- if (length <= 0)
- return 0;
+ /**
+ * cursorPos indicates the next bytecode whichever exclusive is
+ * true or false.
+ */
+ Gap gap = new Gap();
+ if (length <= 0) {
+ gap.position = pos;
+ gap.length = 0;
+ return gap;
+ }
- int cur = currentPos;
- byte[] c = insertGap(bytecode, pos, length, exclusive,
- get().getExceptionTable(), codeAttr);
- int length2 = c.length - bytecode.length;
- if (cur >= pos)
- currentPos = cur + length2;
+ byte[] c;
+ int length2;
+ if (bytecode.length + length > Short.MAX_VALUE) {
+ // currentPos might change after calling insertGapCore0w().
+ c = insertGapCore0w(bytecode, pos, length, exclusive,
+ get().getExceptionTable(), codeAttr, gap);
+ pos = gap.position;
+ length2 = length; // == gap.length
+ }
+ else {
+ int cur = currentPos;
+ c = insertGapCore0(bytecode, pos, length, exclusive,
+ get().getExceptionTable(), codeAttr);
+ // insertGapCore0() never changes pos.
+ length2 = c.length - bytecode.length;
+ gap.position = pos;
+ gap.length = length2;
+ if (cur >= pos)
+ currentPos = cur + length2;
+
+ if (mark > pos || (mark == pos && exclusive))
+ mark += length2;
+ }
codeAttr.setCode(c);
bytecode = c;
endPos = getCodeLength();
updateCursors(pos, length2);
- return length2;
+ return gap;
}
/**
// methods for implementing insertGap().
- /* If "where" is the beginning of a block statement, then the inserted
- * gap is also included in the block statement.
+ static class AlignmentException extends Exception {}
+
+ /**
+ * insertGapCore0() inserts a gap (some NOPs).
+ * It cannot handle a long code sequence more than 32K. All branch offsets must be
+ * signed 16bits.
+ *
+ * If "where" is the beginning of a block statement and exclusive is false,
+ * then the inserted gap is also included in the block statement.
* "where" must indicate the first byte of an opcode.
* The inserted gap is filled with NOP. gapLength may be extended to
* a multiple of 4.
+ *
+ * This method was also called from CodeAttribute.LdcEntry.doit().
+ *
+ * @param where It must indicate the first byte of an opcode.
*/
- static byte[] insertGap(byte[] code, int where, int gapLength,
- boolean exclusive, ExceptionTable etable, CodeAttribute ca)
+ static byte[] insertGapCore0(byte[] code, int where, int gapLength,
+ boolean exclusive, ExceptionTable etable, CodeAttribute ca)
throws BadBytecode
{
if (gapLength <= 0)
return code;
try {
- return insertGap0(code, where, gapLength, exclusive, etable, ca);
+ return insertGapCore1(code, where, gapLength, exclusive, etable, ca);
}
catch (AlignmentException e) {
try {
- return insertGap0(code, where, (gapLength + 3) & ~3,
+ return insertGapCore1(code, where, (gapLength + 3) & ~3,
exclusive, etable, ca);
}
catch (AlignmentException e2) {
}
}
- private static byte[] insertGap0(byte[] code, int where, int gapLength,
- boolean exclusive, ExceptionTable etable,
- CodeAttribute ca)
+ private static byte[] insertGapCore1(byte[] code, int where, int gapLength,
+ boolean exclusive, ExceptionTable etable,
+ CodeAttribute ca)
throws BadBytecode, AlignmentException
{
int codeLength = code.length;
}
}
+
private static int copyGapBytes(byte[] newcode, int j, byte[] code, int i, int iEnd) {
switch (iEnd - i) {
case 4:
if (where < target || (exclusive && where == target))
offset += gapLength;
}
+ else if (i == where) {
+ if (target < where && exclusive)
+ offset -= gapLength;
+ else if (where < target && !exclusive)
+ offset += gapLength;
+ }
else
if (target < where || (!exclusive && where == target))
offset -= gapLength;
return offset;
}
-}
+ static class Pointers {
+ int cursor;
+ int mark0, mark;
+ ExceptionTable etable;
+ LineNumberAttribute line;
+ LocalVariableAttribute vars, types;
+ StackMapTable stack;
+
+ Pointers(int cur, int m, int m0, ExceptionTable et, CodeAttribute ca) {
+ cursor = cur;
+ mark = m;
+ mark0 = m0;
+ etable = et; // non null
+ line = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
+ vars = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag);
+ types = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.typeTag);
+ stack = (StackMapTable)ca.getAttribute(StackMapTable.tag);
+ }
+
+ void shiftPc(int where, int gapLength, boolean exclusive) throws BadBytecode {
+ if (where < cursor || (where == cursor && exclusive))
+ cursor += gapLength;
+
+ if (where < mark || (where == mark && exclusive))
+ mark += gapLength;
+
+ if (where < mark0 || (where == mark0 && exclusive))
+ mark0 += gapLength;
+
+ etable.shiftPc(where, gapLength, exclusive);
+ if (line != null)
+ line.shiftPc(where, gapLength, exclusive);
-class AlignmentException extends Exception {
+ if (vars != null)
+ vars.shiftPc(where, gapLength, exclusive);
+
+ if (types != null)
+ vars.shiftPc(where, gapLength, exclusive);
+
+ if (stack != null)
+ stack.shiftPc(where, gapLength, exclusive);
+ }
+ }
+
+ /*
+ * This method is called from CodeAttribute.LdcEntry.doit().
+ */
+ static byte[] changeLdcToLdcW(byte[] code, ExceptionTable etable,
+ CodeAttribute ca, CodeAttribute.LdcEntry ldcs)
+ throws BadBytecode
+ {
+ ArrayList jumps = makeJumpList(code, code.length);
+ while (ldcs != null) {
+ addLdcW(ldcs, jumps);
+ ldcs = ldcs.next;
+ }
+
+ Pointers pointers = new Pointers(0, 0, 0, etable, ca);
+ byte[] r = insertGap2w(code, 0, 0, false, jumps, pointers);
+ return r;
+ }
+
+ private static void addLdcW(CodeAttribute.LdcEntry ldcs, ArrayList jumps) {
+ int where = ldcs.where;
+ LdcW ldcw = new LdcW(where, ldcs.index);
+ int s = jumps.size();
+ for (int i = 0; i < s; i++)
+ if (where < ((Branch)jumps.get(i)).orgPos) {
+ jumps.add(i, ldcw);
+ return;
+ }
+
+ jumps.add(ldcw);
+ }
+
+ /*
+ * insertGapCore0w() can handle a long code sequence more than 32K.
+ * It guarantees that the length of the inserted gap (NOPs) is equal to
+ * gapLength. No other NOPs except some NOPs following TABLESWITCH or
+ * LOOKUPSWITCH will not be inserted.
+ *
+ * Note: currentPos might be moved.
+ *
+ * @param where It must indicate the first byte of an opcode.
+ * @param newWhere It contains the updated index of the position where a gap
+ * is inserted and the length of the gap.
+ * It must not be null.
+ */
+ private byte[] insertGapCore0w(byte[] code, int where, int gapLength, boolean exclusive,
+ ExceptionTable etable, CodeAttribute ca, Gap newWhere)
+ throws BadBytecode
+ {
+ if (gapLength <= 0)
+ return code;
+
+ ArrayList jumps = makeJumpList(code, code.length);
+ Pointers pointers = new Pointers(currentPos, mark, where, etable, ca);
+ byte[] r = insertGap2w(code, where, gapLength, exclusive, jumps, pointers);
+ currentPos = pointers.cursor;
+ mark = pointers.mark;
+ int where2 = pointers.mark0;
+ if (where2 == currentPos && !exclusive)
+ currentPos += gapLength;
+
+ if (exclusive)
+ where2 -= gapLength;
+
+ newWhere.position = where2;
+ newWhere.length = gapLength;
+ return r;
+ }
+
+ private static byte[] insertGap2w(byte[] code, int where, int gapLength,
+ boolean exclusive, ArrayList jumps, Pointers ptrs)
+ throws BadBytecode
+ {
+ int n = jumps.size();
+ if (gapLength > 0) {
+ ptrs.shiftPc(where, gapLength, exclusive);
+ for (int i = 0; i < n; i++)
+ ((Branch)jumps.get(i)).shift(where, gapLength, exclusive);
+ }
+
+ boolean unstable = true;
+ do {
+ while (unstable) {
+ unstable = false;
+ for (int i = 0; i < n; i++) {
+ Branch b = (Branch)jumps.get(i);
+ if (b.expanded()) {
+ unstable = true;
+ int p = b.pos;
+ int delta = b.deltaSize();
+ ptrs.shiftPc(p, delta, false);
+ for (int j = 0; j < n; j++)
+ ((Branch)jumps.get(j)).shift(p, delta, false);
+ }
+ }
+ }
+
+ for (int i = 0; i < n; i++) {
+ Branch b = (Branch)jumps.get(i);
+ int diff = b.gapChanged();
+ if (diff > 0) {
+ unstable = true;
+ int p = b.pos;
+ ptrs.shiftPc(p, diff, false);
+ for (int j = 0; j < n; j++)
+ ((Branch)jumps.get(j)).shift(p, diff, false);
+ }
+ }
+ } while (unstable);
+
+ return makeExapndedCode(code, jumps, where, gapLength);
+ }
+
+ private static ArrayList makeJumpList(byte[] code, int endPos)
+ throws BadBytecode
+ {
+ ArrayList jumps = new ArrayList();
+ int nextPos;
+ for (int i = 0; i < endPos; i = nextPos) {
+ nextPos = nextOpcode(code, i);
+ int inst = code[i] & 0xff;
+ // if<cond>, if_icmp<cond>, if_acmp<cond>, goto, jsr
+ if ((153 <= inst && inst <= 168)
+ || inst == IFNULL || inst == IFNONNULL) {
+ /* 2bytes *signed* offset */
+ int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff);
+ Branch b;
+ if (inst == GOTO || inst == JSR)
+ b = new Jump16(i, offset);
+ else
+ b = new If16(i, offset);
+
+ jumps.add(b);
+ }
+ else if (inst == GOTO_W || inst == JSR_W) {
+ /* 4bytes offset */
+ int offset = ByteArray.read32bit(code, i + 1);
+ jumps.add(new Jump32(i, offset));
+ }
+ else if (inst == TABLESWITCH) {
+ int i2 = (i & ~3) + 4; // 0-3 byte padding
+ int defaultbyte = ByteArray.read32bit(code, i2);
+ int lowbyte = ByteArray.read32bit(code, i2 + 4);
+ int highbyte = ByteArray.read32bit(code, i2 + 8);
+ int i0 = i2 + 12;
+ int size = highbyte - lowbyte + 1;
+ int[] offsets = new int[size];
+ for (int j = 0; j < size; j++) {
+ offsets[j] = ByteArray.read32bit(code, i0);
+ i0 += 4;
+ }
+
+ jumps.add(new Table(i, defaultbyte, lowbyte, highbyte, offsets));
+ }
+ else if (inst == LOOKUPSWITCH) {
+ int i2 = (i & ~3) + 4; // 0-3 byte padding
+ int defaultbyte = ByteArray.read32bit(code, i2);
+ int npairs = ByteArray.read32bit(code, i2 + 4);
+ int i0 = i2 + 8;
+ int[] matches = new int[npairs];
+ int[] offsets = new int[npairs];
+ for (int j = 0; j < npairs; j++) {
+ matches[j] = ByteArray.read32bit(code, i0);
+ offsets[j] = ByteArray.read32bit(code, i0 + 4);
+ i0 += 8;
+ }
+
+ jumps.add(new Lookup(i, defaultbyte, matches, offsets));
+ }
+ }
+
+ return jumps;
+ }
+
+ private static byte[] makeExapndedCode(byte[] code, ArrayList jumps,
+ int where, int gapLength)
+ throws BadBytecode
+ {
+ int n = jumps.size();
+ int size = code.length + gapLength;
+ for (int i = 0; i < n; i++) {
+ Branch b = (Branch)jumps.get(i);
+ size += b.deltaSize();
+ }
+
+ byte[] newcode = new byte[size];
+ int src = 0, dest = 0, bindex = 0;
+ int len = code.length;
+ Branch b;
+ int bpos;
+ if (0 < n) {
+ b = (Branch)jumps.get(0);
+ bpos = b.orgPos;
+ }
+ else {
+ b = null;
+ bpos = len; // src will be never equal to bpos
+ }
+
+ while (src < len) {
+ if (src == where) {
+ int pos2 = dest + gapLength;
+ while (dest < pos2)
+ newcode[dest++] = NOP;
+ }
+
+ if (src != bpos)
+ newcode[dest++] = code[src++];
+ else {
+ int s = b.write(src, code, dest, newcode);
+ src += s;
+ dest += s + b.deltaSize();
+ if (++bindex < n) {
+ b = (Branch)jumps.get(bindex);
+ bpos = b.orgPos;
+ }
+ else {
+ b = null;
+ bpos = len;
+ }
+ }
+ }
+
+ return newcode;
+ }
+
+ static abstract class Branch {
+ int pos, orgPos;
+ Branch(int p) { pos = orgPos = p; }
+ void shift(int where, int gapLength, boolean exclusive) {
+ if (where < pos || (where == pos && exclusive))
+ pos += gapLength;
+ }
+
+ boolean expanded() { return false; }
+ int gapChanged() { return 0; }
+ int deltaSize() { return 0; } // newSize - oldSize
+
+ // This returns the original instruction size.
+ abstract int write(int srcPos, byte[] code, int destPos, byte[] newcode);
+ }
+
+ /* used by changeLdcToLdcW() and CodeAttribute.LdcEntry.
+ */
+ static class LdcW extends Branch {
+ int index;
+ boolean state;
+ LdcW(int p, int i) {
+ super(p);
+ index = i;
+ state = true;
+ }
+
+ boolean expanded() {
+ if (state) {
+ state = false;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ int deltaSize() { return 1; }
+
+ int write(int srcPos, byte[] code, int destPos, byte[] newcode) {
+ newcode[destPos] = LDC_W;
+ ByteArray.write16bit(index, newcode, destPos + 1);
+ return 2;
+ }
+ }
+
+ static abstract class Branch16 extends Branch {
+ int offset;
+ int state;
+ static final int BIT16 = 0;
+ static final int EXPAND = 1;
+ static final int BIT32 = 2;
+
+ Branch16(int p, int off) {
+ super(p);
+ offset = off;
+ state = BIT16;
+ }
+
+ void shift(int where, int gapLength, boolean exclusive) {
+ offset = newOffset(pos, offset, where, gapLength, exclusive);
+ super.shift(where, gapLength, exclusive);
+ if (state == BIT16)
+ if (offset < Short.MIN_VALUE || Short.MAX_VALUE < offset)
+ state = EXPAND;
+ }
+
+ boolean expanded() {
+ if (state == EXPAND) {
+ state = BIT32;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ abstract int deltaSize();
+ abstract void write32(int src, byte[] code, int dest, byte[] newcode);
+
+ int write(int src, byte[] code, int dest, byte[] newcode) {
+ if (state == BIT32)
+ write32(src, code, dest, newcode);
+ else {
+ newcode[dest] = code[src];
+ ByteArray.write16bit(offset, newcode, dest + 1);
+ }
+
+ return 3;
+ }
+ }
+
+ // GOTO or JSR
+ static class Jump16 extends Branch16 {
+ Jump16(int p, int off) {
+ super(p, off);
+ }
+
+ int deltaSize() {
+ return state == BIT32 ? 2 : 0;
+ }
+
+ void write32(int src, byte[] code, int dest, byte[] newcode) {
+ newcode[dest] = (byte)(((code[src] & 0xff) == GOTO) ? GOTO_W : JSR_W);
+ ByteArray.write32bit(offset, newcode, dest + 1);
+ }
+ }
+
+ // if<cond>, if_icmp<cond>, or if_acmp<cond>
+ static class If16 extends Branch16 {
+ If16(int p, int off) {
+ super(p, off);
+ }
+
+ int deltaSize() {
+ return state == BIT32 ? 5 : 0;
+ }
+
+ void write32(int src, byte[] code, int dest, byte[] newcode) {
+ newcode[dest] = (byte)opcode(code[src] & 0xff);
+ newcode[dest + 1] = 0;
+ newcode[dest + 2] = 8; // branch_offset = 8
+ newcode[dest + 3] = (byte)GOTO_W;
+ ByteArray.write32bit(offset - 3, newcode, dest + 4);
+ }
+
+ int opcode(int op) {
+ if (op == IFNULL)
+ return IFNONNULL;
+ else if (op == IFNONNULL)
+ return IFNULL;
+ else {
+ if (((op - IFEQ) & 1) == 0)
+ return op + 1;
+ else
+ return op - 1;
+ }
+ }
+ }
+
+ static class Jump32 extends Branch {
+ int offset;
+
+ Jump32(int p, int off) {
+ super(p);
+ offset = off;
+ }
+
+ void shift(int where, int gapLength, boolean exclusive) {
+ offset = newOffset(pos, offset, where, gapLength, exclusive);
+ super.shift(where, gapLength, exclusive);
+ }
+
+ int write(int src, byte[] code, int dest, byte[] newcode) {
+ newcode[dest] = code[src];
+ ByteArray.write32bit(offset, newcode, dest + 1);
+ return 5;
+ }
+ }
+
+ static abstract class Switcher extends Branch {
+ int gap, defaultByte;
+ int[] offsets;
+
+ Switcher(int pos, int defaultByte, int[] offsets) {
+ super(pos);
+ this.gap = 3 - (pos & 3);
+ this.defaultByte = defaultByte;
+ this.offsets = offsets;
+ }
+
+ void shift(int where, int gapLength, boolean exclusive) {
+ int p = pos;
+ defaultByte = newOffset(p, defaultByte, where, gapLength, exclusive);
+ int num = offsets.length;
+ for (int i = 0; i < num; i++)
+ offsets[i] = newOffset(p, offsets[i], where, gapLength, exclusive);
+
+ super.shift(where, gapLength, exclusive);
+ }
+
+ int gapChanged() {
+ int newGap = 3 - (pos & 3);
+ if (newGap > gap) {
+ int diff = newGap - gap;
+ gap = newGap;
+ return diff;
+ }
+
+ return 0;
+ }
+
+ int deltaSize() {
+ return gap - (3 - (orgPos & 3));
+ }
+
+ int write(int src, byte[] code, int dest, byte[] newcode) {
+ newcode[dest++] = code[src];
+ int padding = 3 - (pos & 3);
+ int nops = gap - padding;
+ while (padding-- > 0)
+ newcode[dest++] = 0;
+
+ ByteArray.write32bit(defaultByte, newcode, dest);
+ int size = write2(dest + 4, newcode);
+ while (nops-- > 0)
+ newcode[dest++] = NOP;
+
+ return 5 + (3 - (orgPos & 3)) + size;
+ }
+
+ abstract int write2(int dest, byte[] newcode);
+ }
+
+ static class Table extends Switcher {
+ int low, high;
+
+ Table(int pos, int defaultByte, int low, int high, int[] offsets) {
+ super(pos, defaultByte, offsets);
+ this.low = low;
+ this.high = high;
+ }
+
+ int write2(int dest, byte[] newcode) {
+ ByteArray.write32bit(low, newcode, dest);
+ ByteArray.write32bit(high, newcode, dest + 4);
+ int n = offsets.length;
+ dest += 8;
+ for (int i = 0; i < n; i++) {
+ ByteArray.write32bit(offsets[i], newcode, dest);
+ dest += 4;
+ }
+
+ return 8 + 4 * n;
+ }
+ }
+
+ static class Lookup extends Switcher {
+ int[] matches;
+
+ Lookup(int pos, int defaultByte, int[] matches, int[] offsets) {
+ super(pos, defaultByte, offsets);
+ this.matches = matches;
+ }
+
+ int write2(int dest, byte[] newcode) {
+ int n = matches.length;
+ ByteArray.write32bit(n, newcode, dest);
+ dest += 4;
+ for (int i = 0; i < n; i++) {
+ ByteArray.write32bit(matches[i], newcode, dest);
+ ByteArray.write32bit(offsets[i], newcode, dest + 4);
+ dest += 8;
+ }
+
+ return 4 + 8 * n;
+ }
+ }
}