All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javassist.bytecode.CodeIterator Maven / Gradle / Ivy

Go to download

Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation simple. It is a class library for editing bytecodes in Java.

The newest version!
/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package javassist.bytecode;

/**
 * An iterator for editing a code attribute.
 *
 * 

If there are multiple CodeIterators referring to the * same Code_attribute, then inserting a gap by one * CodeIterator will break the other * CodeIterator. * *

This iterator does not provide remove(). * If a piece of code in a Code_attribute is unnecessary, * it should be overwritten with NOP. * * @see CodeAttribute#iterator() */ public class CodeIterator implements Opcode { protected CodeAttribute codeAttr; protected byte[] bytecode; protected int endPos; protected int currentPos; protected CodeIterator(CodeAttribute ca) { codeAttr = ca; bytecode = ca.getCode(); begin(); } /** * Moves to the first instruction. */ public void begin() { currentPos = 0; endPos = getCodeLength(); } /** * Moves to the given index. * *

The index of the next instruction is set to the given index. * The successive call to next() * returns the index that has been given to move(). * *

Note that the index is into the byte array returned by * get().getCode(). * * @see CodeAttribute#getCode() */ public void move(int index) { currentPos = index; } /** * Returns a Code attribute read with this iterator. */ public CodeAttribute get() { return codeAttr; } /** * Returns code_length of Code_attribute. */ public int getCodeLength() { return bytecode.length; } /** * Returns the unsigned 8bit value at the given index. */ public int byteAt(int index) { return bytecode[index] & 0xff; } /** * Writes an 8bit value at the given index. */ public void writeByte(int value, int index) { bytecode[index] = (byte)value; } /** * Returns the unsigned 16bit value at the given index. */ public int u16bitAt(int index) { return ByteArray.readU16bit(bytecode, index); } /** * Returns the signed 16bit value at the given index. */ public int s16bitAt(int index) { return ByteArray.readS16bit(bytecode, index); } /** * Writes a 16 bit integer at the index. */ public void write16bit(int value, int index) { ByteArray.write16bit(value, bytecode, index); } /** * Returns the signed 32bit value at the given index. */ public int s32bitAt(int index) { return ByteArray.read32bit(bytecode, index); } /** * Writes a 32bit integer at the index. */ public void write32bit(int value, int index) { ByteArray.write32bit(value, bytecode, index); } /** * Writes a byte array at the index. * * @param code may be a zero-length array. */ public void write(byte[] code, int index) { int len = code.length; for (int j = 0; j < len; ++j) bytecode[index++] = code[j]; } /** * Returns true if there is more instructions. */ public boolean hasNext() { return currentPos < endPos; } /** * Returns the index of the next instruction * (not the operand following the current opcode). * *

Note that the index is into the byte array returned by * get().getCode(). * * @see CodeAttribute#getCode() * @see CodeIterator#byteAt(int) */ public int next() throws BadBytecode { int pos = currentPos; currentPos = nextOpcode(bytecode, pos); return pos; } /** * Obtains the value that the next call * to next() will return. * *

This method is side-effects free. * Successive calls to lookAhead() return the * same value until next() is called. */ public int lookAhead() { return currentPos; } /** * Moves to the instruction for * either super() or this(). * *

This method skips all the instructions for computing arguments * to super() or this(), which should be * placed at the beginning of a constructor body. * *

This method returns the index of INVOKESPECIAL instruction * executing super() or this(). * A successive call to next() returns the * index of the next instruction following that INVOKESPECIAL. * *

This method works only for a constructor. * * @return the index of the INVOKESPECIAL instruction, or -1 * if a constructor invocation is not found. */ public int skipConstructor() throws BadBytecode { return skipSuperConstructor0(-1); } /** * Moves to the instruction for super(). * *

This method skips all the instructions for computing arguments to * super(), which should be * placed at the beginning of a constructor body. * *

This method returns the index of INVOKESPECIAL instruction * executing super(). * A successive call to next() returns the * index of the next instruction following that INVOKESPECIAL. * *

This method works only for a constructor. * * @return the index of the INVOKESPECIAL instruction, or -1 * if a super constructor invocation is not found * but this() is found. */ public int skipSuperConstructor() throws BadBytecode { return skipSuperConstructor0(0); } /** * Moves to the instruction for this(). * *

This method skips all the instructions for computing arguments to * this(), which should be * placed at the beginning of a constructor body. * *

This method returns the index of INVOKESPECIAL instruction * executing this(). * A successive call to next() returns the * index of the next instruction following that INVOKESPECIAL. * *

This method works only for a constructor. * * @return the index of the INVOKESPECIAL instruction, or -1 * if a explicit constructor invocation is not found * but super() is found. */ public int skipThisConstructor() throws BadBytecode { return skipSuperConstructor0(1); } /* skipSuper 1: this(), 0: super(), -1: both. */ private int skipSuperConstructor0(int skipThis) throws BadBytecode { begin(); ConstPool cp = codeAttr.getConstPool(); String thisClassName = codeAttr.getDeclaringClass(); int nested = 0; while (hasNext()) { int index = next(); int c = byteAt(index); if (c == NEW) ++nested; else if (c == INVOKESPECIAL) { int mref = ByteArray.readU16bit(bytecode, index + 1); if (cp.getMethodrefName(mref).equals(MethodInfo.nameInit)) if (--nested < 0) { if (skipThis < 0) return index; String cname = cp.getMethodrefClassName(mref); if (cname.equals(thisClassName) == (skipThis > 0)) return index; else break; } } } begin(); return -1; } /** * Inserts the given bytecode sequence * before the next instruction that would be returned by * next() (not before the instruction returned * by tha last call to next()). * Branch offsets and the exception table are also updated. * *

If the next instruction is at the beginning of a block statement, * then the bytecode is inserted within that block. * *

An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * * @param code inserted bytecode sequence. * @return the index indicating the first byte of the * inserted byte sequence. */ public int insert(byte[] code) throws BadBytecode { int pos = currentPos; insert0(currentPos, code, false); return pos; } /** * Inserts the given bytecode sequence * before the instruction at the given index pos. * Branch offsets and the exception table are also updated. * *

If the instruction at the given index is at the beginning * of a block statement, * then the bytecode is inserted within that block. * *

An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * * @param pos the index at which a byte sequence is inserted. * @param code inserted bytecode sequence. */ public void insert(int pos, byte[] code) throws BadBytecode { insert0(pos, code, false); } /** * Inserts the given bytecode sequence exclusively * before the next instruction that would be returned by * next() (not before the instruction returned * by tha last call to next()). * Branch offsets and the exception table are also updated. * *

If the next instruction is at the beginning of a block statement, * then the bytecode is excluded from that block. * *

An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * * @param code inserted bytecode sequence. * @return the index indicating the first byte of the * inserted byte sequence. */ public int insertEx(byte[] code) throws BadBytecode { int pos = currentPos; insert0(currentPos, code, true); return pos; } /** * Inserts the given bytecode sequence exclusively * before the instruction at the given index pos. * Branch offsets and the exception table are also updated. * *

If the instruction at the given index is at the beginning * of a block statement, * then the bytecode is excluded from that block. * *

An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * * @param pos the index at which a byte sequence is inserted. * @param code inserted bytecode sequence. */ public void insertEx(int pos, byte[] code) throws BadBytecode { insert0(pos, code, true); } private void insert0(int pos, byte[] code, boolean exclusive) throws BadBytecode { int len = code.length; if (len <= 0) return; insertGapCore(pos, len, exclusive); // currentPos will change. for (int j = 0; j < len; ++j) bytecode[pos++] = code[j]; } /** * Inserts a gap * before the next instruction that would be returned by * next() (not before the instruction returned * by tha last call to next()). * 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. * *

If the next instruction is at the beginning of a block statement, * then the gap is inserted within that block. * * @param length gap length * @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; } /** * Inserts a gap in front of the instruction at the given * index pos. * 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. * *

If the instruction at the given index is at the beginning * of a block statement, * then the gap is inserted within that block. * * @param pos the index at which a gap is inserted. * @param length gap length. * @return the length of the inserted gap. * It might be bigger than length. */ public int insertGap(int pos, int length) throws BadBytecode { return insertGapCore(pos, length, false); } /** * Inserts an exclusive gap * before the next instruction that would be returned by * next() (not before the instruction returned * by tha last call to next()). * 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. * *

If the next instruction is at the beginning of a block statement, * then the gap is excluded from that block. * * @param length gap length * @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; } /** * Inserts an exclusive gap in front of the instruction at the given * index pos. * 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. * *

If the instruction at the given index is at the beginning * of a block statement, * then the gap is excluded from that block. * * @param pos the index at which a gap is inserted. * @param length gap length. * @return the length of the inserted gap. * It might be bigger than length. */ public int insertExGap(int pos, int length) throws BadBytecode { return insertGapCore(pos, length, true); } /** * @return the length of the really inserted gap. */ private int insertGapCore(int pos, int length, boolean exclusive) throws BadBytecode { if (length <= 0) return 0; 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; codeAttr.setCode(c); bytecode = c; endPos = getCodeLength(); updateCursors(pos, length2); return length2; } /** * Is called when a gap is inserted. The default implementation is empty. * A subclass can override this method so that cursors will be updated. * * @param pos the position where a gap is inserted. * @param length the length of the gap. */ protected void updateCursors(int pos, int length) { // empty } /** * Copies and inserts the entries in the given exception table * at the beginning of the exception table in the code attribute * edited by this object. * * @param offset the value added to the code positions included * in the entries. */ public void insert(ExceptionTable et, int offset) { codeAttr.getExceptionTable().add(0, et, offset); } /** * Appends the given bytecode sequence at the end. * * @param code the bytecode appended. * @return the position of the first byte of the appended bytecode. */ public int append(byte[] code) { int size = getCodeLength(); int len = code.length; if (len <= 0) return size; appendGap(len); byte[] dest = bytecode; for (int i = 0; i < len; ++i) dest[i + size] = code[i]; return size; } /** * Appends a gap at the end of the bytecode sequence. * * @param gapLength gap length */ public void appendGap(int gapLength) { byte[] code = bytecode; int codeLength = code.length; byte[] newcode = new byte[codeLength + gapLength]; int i; for (i = 0; i < codeLength; ++i) newcode[i] = code[i]; for (i = codeLength; i < codeLength + gapLength; ++i) newcode[i] = NOP; codeAttr.setCode(newcode); bytecode = newcode; endPos = getCodeLength(); } /** * Copies and appends the entries in the given exception table * at the end of the exception table in the code attribute * edited by this object. * * @param offset the value added to the code positions included * in the entries. */ public void append(ExceptionTable et, int offset) { ExceptionTable table = codeAttr.getExceptionTable(); table.add(table.size(), et, offset); } /* opcodeLegth is used for implementing nextOpcode(). */ private static final int opcodeLength[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 5, 0, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, 5, 5 }; // 0 .. UNUSED (186), LOOKUPSWITCH, TABLESWITCH, WIDE /** * Calculates the index of the next opcode. */ static int nextOpcode(byte[] code, int index) throws BadBytecode { int opcode; try { opcode = code[index] & 0xff; } catch (IndexOutOfBoundsException e) { throw new BadBytecode("invalid opcode address"); } try { int len = opcodeLength[opcode]; if (len > 0) return index + len; else if (opcode == WIDE) if (code[index + 1] == (byte)IINC) // WIDE IINC return index + 6; else return index + 4; // WIDE ... else { int index2 = (index & ~3) + 8; if (opcode == LOOKUPSWITCH) { int npairs = ByteArray.read32bit(code, index2); return index2 + npairs * 8 + 4; } else if (opcode == TABLESWITCH) { int low = ByteArray.read32bit(code, index2); int high = ByteArray.read32bit(code, index2 + 4); return index2 + (high - low + 1) * 4 + 8; } // else // throw new BadBytecode(opcode); } } catch (IndexOutOfBoundsException e) { } // opcode is UNUSED or an IndexOutOfBoundsException was thrown. throw new BadBytecode(opcode); } // methods for implementing insertGap(). /* If "where" is the beginning of a block statement, 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. */ static byte[] insertGap(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); } catch (AlignmentException e) { try { return insertGap0(code, where, (gapLength + 3) & ~3, exclusive, etable, ca); } catch (AlignmentException e2) { throw new RuntimeException("fatal error?"); } } } private static byte[] insertGap0(byte[] code, int where, int gapLength, boolean exclusive, ExceptionTable etable, CodeAttribute ca) throws BadBytecode, AlignmentException { int codeLength = code.length; byte[] newcode = new byte[codeLength + gapLength]; insertGap2(code, where, gapLength, codeLength, newcode, exclusive); etable.shiftPc(where, gapLength, exclusive); LineNumberAttribute na = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); if (na != null) na.shiftPc(where, gapLength, exclusive); LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( LocalVariableAttribute.tag); if (va != null) va.shiftPc(where, gapLength, exclusive); LocalVariableAttribute vta = (LocalVariableAttribute)ca.getAttribute( LocalVariableAttribute.typeTag); if (vta != null) vta.shiftPc(where, gapLength, exclusive); StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); if (smt != null) smt.shiftPc(where, gapLength, exclusive); return newcode; } private static void insertGap2(byte[] code, int where, int gapLength, int endPos, byte[] newcode, boolean exclusive) throws BadBytecode, AlignmentException { int nextPos; int i = 0; int j = 0; for (; i < endPos; i = nextPos) { if (i == where) { int j2 = j + gapLength; while (j < j2) newcode[j++] = NOP; } nextPos = nextOpcode(code, i); int inst = code[i] & 0xff; // if, if_icmp, if_acmp, goto, jsr if ((153 <= inst && inst <= 168) || inst == IFNULL || inst == IFNONNULL) { /* 2bytes *signed* offset */ int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff); offset = newOffset(i, offset, where, gapLength, exclusive); newcode[j] = code[i]; ByteArray.write16bit(offset, newcode, j + 1); j += 3; } else if (inst == GOTO_W || inst == JSR_W) { /* 4bytes offset */ int offset = ByteArray.read32bit(code, i + 1); offset = newOffset(i, offset, where, gapLength, exclusive); newcode[j++] = code[i]; ByteArray.write32bit(offset, newcode, j); j += 4; } else if (inst == TABLESWITCH) { if (i != j && (gapLength & 3) != 0) throw new AlignmentException(); int i0 = i; int i2 = (i & ~3) + 4; // 0-3 byte padding while (i0 < i2) newcode[j++] = code[i0++]; int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2), where, gapLength, exclusive); ByteArray.write32bit(defaultbyte, newcode, j); int lowbyte = ByteArray.read32bit(code, i2 + 4); ByteArray.write32bit(lowbyte, newcode, j + 4); int highbyte = ByteArray.read32bit(code, i2 + 8); ByteArray.write32bit(highbyte, newcode, j + 8); j += 12; i0 = i2 + 12; i2 = i0 + (highbyte - lowbyte + 1) * 4; while (i0 < i2) { int offset = newOffset(i, ByteArray.read32bit(code, i0), where, gapLength, exclusive); ByteArray.write32bit(offset, newcode, j); j += 4; i0 += 4; } } else if (inst == LOOKUPSWITCH) { if (i != j && (gapLength & 3) != 0) throw new AlignmentException(); int i0 = i; int i2 = (i & ~3) + 4; // 0-3 byte padding while (i0 < i2) newcode[j++] = code[i0++]; int defaultbyte = newOffset(i, ByteArray.read32bit(code, i2), where, gapLength, exclusive); ByteArray.write32bit(defaultbyte, newcode, j); int npairs = ByteArray.read32bit(code, i2 + 4); ByteArray.write32bit(npairs, newcode, j + 4); j += 8; i0 = i2 + 8; i2 = i0 + npairs * 8; while (i0 < i2) { ByteArray.copy32bit(code, i0, newcode, j); int offset = newOffset(i, ByteArray.read32bit(code, i0 + 4), where, gapLength, exclusive); ByteArray.write32bit(offset, newcode, j + 4); j += 8; i0 += 8; } } else while (i < nextPos) newcode[j++] = code[i++]; } } private static int newOffset(int i, int offset, int where, int gapLength, boolean exclusive) { int target = i + offset; if (i < where) { if (where < target || (exclusive && where == target)) offset += gapLength; } else if (target < where || (!exclusive && where == target)) offset -= gapLength; return offset; } } class AlignmentException extends Exception { }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy