FileDocCategorySizeDatePackage
CodeWriter.javaAPI DocGlassfish v2 API61738Thu Mar 02 11:51:12 GMT 2006oracle.toplink.libraries.asm

CodeWriter

public class CodeWriter extends Object implements CodeVisitor
A {@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.
author
Eric Bruneton

Fields Summary
static final boolean
CHECK
true if preconditions must be checked at runtime or not.
CodeWriter
next
Next code writer (see {@link ClassWriter#firstMethod firstMethod}).
private ClassWriter
cw
The class writer to which this method must be added.
private int
name
The index of the constant pool item that contains the name of this method.
private int
desc
The index of the constant pool item that contains the descriptor of this method.
private int
access
Access flags of this method.
private int
maxStack
Maximum stack size of this method.
private int
maxLocals
Maximum number of local variables for this method.
private ByteVector
code
The bytecode of this method.
private int
catchCount
Number of entries in the catch table of this method.
private ByteVector
catchTable
The catch table of this method.
private int
exceptionCount
Number of exceptions that can be thrown by this method.
private int[]
exceptions
The 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
attrs
The non standard attributes of the method.
private int
localVarCount
Number of entries in the LocalVariableTable attribute.
private ByteVector
localVar
The LocalVariableTable attribute.
private int
lineNumberCount
Number of entries in the LineNumberTable attribute.
private ByteVector
lineNumber
The LineNumberTable attribute.
private Attribute
cattrs
The non standard attributes of the method's code.
private boolean
resize
Indicates if some jump instructions are too small and need to be resized.
private final boolean
computeMaxs
true if the maximum stack size and number of local variables must be automatically computed.
private int
stackSize
The (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
maxStackSize
The (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
currentBlock
The current basic block. This block is the basic block to which the next instruction to be visited must be added.
private Label
blockStack
The 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[]
SIZE
The 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
head
The 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
tail
The 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
pool
The 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.

param
cw the class writer in which the method must be added.
param
computeMaxs true if the maximum stack size and number of local variables must be automatically computed.


  // --------------------------------------------------------------------------
  // 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 voidaddSuccessor(int stackSize, oracle.toplink.libraries.asm.Label successor)
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.

    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 intgetArgumentsAndReturnSizes(java.lang.String desc)
Computes the size of the arguments and of the return value of a method.

param
desc the descriptor of a method.
return
the size of the arguments of the method (plus one for the implicit this argument), argSize, and the size of its return value, retSize, packed into a single int i = (argSize << 2) | retSize (argSize is therefore equal to i >> 2, and retSize to i & 0x03).

    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
the current bytecode of this method. The bytecode is contained between the index 0 (inclusive) and the index {@link #getCodeSize getCodeSize} (exclusive).

    return code.data;
  
public intgetCodeSize()
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
the current size of the bytecode of this method.

    return code.length;
  
static intgetNewOffset(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').

param
indexes current positions of the instructions to be resized. Each instruction must be designated by the index of its last byte, plus one (or, in other words, by the index of the first byte of the next instruction).
param
sizes the number of bytes to be added to the above instructions. More precisely, for each i < len, sizes[i] bytes will be added at the end of the instruction designated by indexes[i] or, if sizes[i] is negative, the last |sizes[i]| bytes of the instruction will be removed (the instruction size must not become negative or null).
param
begin index of the first byte of the source instruction.
param
end index of the first byte of the target instruction.
return
the future value of the given bytecode offset.

    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 intgetSize()
Returns the size of the bytecode of this method.

return
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 voidinit(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.

param
access the method's access flags (see {@link Constants}).
param
name the method's name.
param
desc the method's descriptor (see {@link Type Type}).
param
exceptions the internal names of the method's exceptions. May be null.
param
attrs the non standard attributes of the 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 voidput(oracle.toplink.libraries.asm.ByteVector out)
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.

    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 intreadInt(byte[] b, int index)
Reads a signed int value in the given byte array.

param
b a byte array.
param
index the start index of the value to be read.
return
the read value.

    return ((b[index] & 0xFF) << 24) |
           ((b[index + 1] & 0xFF) << 16) |
           ((b[index + 2] & 0xFF) << 8) |
           (b[index + 3] & 0xFF);
  
static shortreadShort(byte[] b, int index)
Reads a signed short value in the given byte array.

param
b a byte array.
param
index the start index of the value to be read.
return
the read value.

    return (short)(((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
  
static intreadUnsignedShort(byte[] b, int index)
Reads an unsigned short value in the given byte array.

param
b a byte array.
param
index the start index of the value to be read.
return
the read value.

    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.

param
indexes current positions of the instructions to be resized. Each instruction must be designated by the index of its last byte, plus one (or, in other words, by the index of the first byte of the next instruction).
param
sizes the number of bytes to be added to the above instructions. More precisely, for each i < len, sizes[i] bytes will be added at the end of the instruction designated by indexes[i] or, if sizes[i] is negative, the last |sizes[i]| bytes of the instruction will be removed (the instruction size must not 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 indexes.length and sizes.length.
return
the indexes array, which now contains the new positions of the resized instructions (designated as above).

    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 voidvisitAttribute(oracle.toplink.libraries.asm.Attribute attr)

    attr.next = cattrs;
    cattrs = attr;
  
public voidvisitFieldInsn(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 voidvisitIincInsn(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 voidvisitInsn(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 voidvisitIntInsn(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 voidvisitJumpInsn(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 voidvisitLabel(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 voidvisitLdcInsn(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 voidvisitLineNumber(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 voidvisitLocalVariable(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 voidvisitLookupSwitchInsn(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 voidvisitMaxs(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 voidvisitMethodInsn(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 voidvisitMultiANewArrayInsn(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 voidvisitTableSwitchInsn(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 voidvisitTryCatchBlock(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 voidvisitTypeInsn(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 voidvisitVarInsn(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 voidwriteShort(byte[] b, int index, int s)
Writes a short value in the given byte array.

param
b a byte array.
param
index where the first byte of the short value must be written.
param
s the value to be written in the given byte array.

    b[index] = (byte)(s >>> 8);
    b[index + 1] = (byte)s;