diff options
Diffstat (limited to 'src/jvm/clojure/asm/MethodWriter.java')
-rw-r--r-- | src/jvm/clojure/asm/MethodWriter.java | 3029 |
1 files changed, 3029 insertions, 0 deletions
diff --git a/src/jvm/clojure/asm/MethodWriter.java b/src/jvm/clojure/asm/MethodWriter.java new file mode 100644 index 00000000..19239738 --- /dev/null +++ b/src/jvm/clojure/asm/MethodWriter.java @@ -0,0 +1,3029 @@ +/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A {@link MethodVisitor} that generates methods in bytecode form. 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
+ * @author Eugene Kuleshov
+ */
+class MethodWriter implements MethodVisitor{
+
+/**
+ * Pseudo access flag used to denote constructors.
+ */
+final static int ACC_CONSTRUCTOR = 262144;
+
+/**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is zero.
+ */
+final static int SAME_FRAME = 0; // to 63 (0-3f)
+
+/**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is 1
+ */
+final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
+
+/**
+ * Reserved for future use
+ */
+final static int RESERVED = 128;
+
+/**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is 1. Offset is bigger then 63;
+ */
+final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
+
+/**
+ * Frame where current locals are the same as the locals in the previous
+ * frame, except that the k last locals are absent. The value of k is given
+ * by the formula 251-frame_type.
+ */
+final static int CHOP_FRAME = 248; // to 250 (f8-fA)
+
+/**
+ * Frame has exactly the same locals as the previous stack map frame and
+ * number of stack items is zero. Offset is bigger then 63;
+ */
+final static int SAME_FRAME_EXTENDED = 251; // fb
+
+/**
+ * Frame where current locals are the same as the locals in the previous
+ * frame, except that k additional locals are defined. The value of k is
+ * given by the formula frame_type-251.
+ */
+final static int APPEND_FRAME = 252; // to 254 // fc-fe
+
+/**
+ * Full frame
+ */
+final static int FULL_FRAME = 255; // ff
+
+/**
+ * Indicates that the stack map frames must be recomputed from scratch. In
+ * this case the maximum stack size and number of local variables is also
+ * recomputed from scratch.
+ *
+ * @see #compute
+ */
+private final static int FRAMES = 0;
+
+/**
+ * Indicates that the maximum stack size and number of local variables must
+ * be automatically computed.
+ *
+ * @see #compute
+ */
+private final static int MAXS = 1;
+
+/**
+ * Indicates that nothing must be automatically computed.
+ *
+ * @see #compute
+ */
+private final static int NOTHING = 2;
+
+/**
+ * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
+ */
+MethodWriter next;
+
+/**
+ * The class writer to which this method must be added.
+ */
+ClassWriter cw;
+
+/**
+ * Access flags of this method.
+ */
+private int access;
+
+/**
+ * The index of the constant pool item that contains the name of this
+ * method.
+ */
+private int name;
+
+/**
+ * The index of the constant pool item that contains the descriptor of this
+ * method.
+ */
+private int desc;
+
+/**
+ * The descriptor of this method.
+ */
+private String descriptor;
+
+/**
+ * The signature of this method.
+ */
+String signature;
+
+/**
+ * If not zero, indicates that the code of this method must be copied from
+ * the ClassReader associated to this writer in <code>cw.cr</code>. More
+ * precisely, this field gives the index of the first byte to copied from
+ * <code>cw.cr.b</code>.
+ */
+int classReaderOffset;
+
+/**
+ * If not zero, indicates that the code of this method must be copied from
+ * the ClassReader associated to this writer in <code>cw.cr</code>. More
+ * precisely, this field gives the number of bytes to copied from
+ * <code>cw.cr.b</code>.
+ */
+int classReaderLength;
+
+/**
+ * Number of exceptions that can be thrown by this method.
+ */
+int exceptionCount;
+
+/**
+ * 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.
+ */
+int[] exceptions;
+
+/**
+ * The annotation default attribute of this method. May be <tt>null</tt>.
+ */
+private ByteVector annd;
+
+/**
+ * The runtime visible annotations of this method. May be <tt>null</tt>.
+ */
+private AnnotationWriter anns;
+
+/**
+ * The runtime invisible annotations of this method. May be <tt>null</tt>.
+ */
+private AnnotationWriter ianns;
+
+/**
+ * The runtime visible parameter annotations of this method. May be
+ * <tt>null</tt>.
+ */
+private AnnotationWriter[] panns;
+
+/**
+ * The runtime invisible parameter annotations of this method. May be
+ * <tt>null</tt>.
+ */
+private AnnotationWriter[] ipanns;
+
+/**
+ * The non standard attributes of the method.
+ */
+private Attribute attrs;
+
+/**
+ * The bytecode of this method.
+ */
+private ByteVector code = new ByteVector();
+
+/**
+ * Maximum stack size of this method.
+ */
+private int maxStack;
+
+/**
+ * Maximum number of local variables for this method.
+ */
+private int maxLocals;
+
+/**
+ * Number of stack map frames in the StackMapTable attribute.
+ */
+private int frameCount;
+
+/**
+ * The StackMapTable attribute.
+ */
+private ByteVector stackMap;
+
+/**
+ * The offset of the last frame that was written in the StackMapTable
+ * attribute.
+ */
+private int previousFrameOffset;
+
+/**
+ * The last frame that was written in the StackMapTable attribute.
+ *
+ * @see #frame
+ */
+private int[] previousFrame;
+
+/**
+ * Index of the next element to be added in {@link #frame}.
+ */
+private int frameIndex;
+
+/**
+ * The current stack map frame. The first element contains the offset of the
+ * instruction to which the frame corresponds, the second element is the
+ * number of locals and the third one is the number of stack elements. The
+ * local variables start at index 3 and are followed by the operand stack
+ * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =
+ * nStack, frame[3] = nLocal. All types are encoded as integers, with the
+ * same format as the one used in {@link Label}, but limited to BASE types.
+ */
+private int[] frame;
+
+/**
+ * Number of elements in the exception handler list.
+ */
+private int handlerCount;
+
+/**
+ * The first element in the exception handler list.
+ */
+private Handler firstHandler;
+
+/**
+ * The last element in the exception handler list.
+ */
+private Handler lastHandler;
+
+/**
+ * Number of entries in the LocalVariableTable attribute.
+ */
+private int localVarCount;
+
+/**
+ * The LocalVariableTable attribute.
+ */
+private ByteVector localVar;
+
+/**
+ * Number of entries in the LocalVariableTypeTable attribute.
+ */
+private int localVarTypeCount;
+
+/**
+ * The LocalVariableTypeTable attribute.
+ */
+private ByteVector localVarType;
+
+/**
+ * Number of entries in the LineNumberTable attribute.
+ */
+private int lineNumberCount;
+
+/**
+ * The LineNumberTable attribute.
+ */
+private ByteVector lineNumber;
+
+/**
+ * The non standard attributes of the method's code.
+ */
+private Attribute cattrs;
+
+/**
+ * Indicates if some jump instructions are too small and need to be resized.
+ */
+private boolean resize;
+
+/**
+ * Indicates if the instructions contain at least one JSR instruction.
+ */
+private boolean jsr;
+
+// ------------------------------------------------------------------------
+
+/*
+ * Fields for the control flow graph analysis algorithm (used to compute the
+ * maximum stack size). A control flow graph contains one node per "basic
+ * block", and one edge per "jump" from one basic block to another. Each
+ * node (i.e., each basic block) is represented by the Label object that
+ * corresponds to the first instruction of this basic block. Each node also
+ * stores the list of its successors in the graph, as a linked list of Edge
+ * objects.
+ */
+
+/**
+ * Indicates what must be automatically computed.
+ *
+ * @see FRAMES
+ * @see MAXS
+ * @see NOTHING
+ */
+private int compute;
+
+/**
+ * A list of labels. This list is the list of basic blocks in the method,
+ * i.e. a list of Label objects linked to each other by their
+ * {@link Label#successor} field, in the order they are visited by
+ * {@link visitLabel}, and starting with the first basic block.
+ */
+private Label labels;
+
+/**
+ * The previous basic block.
+ */
+private Label previousBlock;
+
+/**
+ * The current basic block.
+ */
+private Label currentBlock;
+
+/**
+ * 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#inputStackTop beginStackSize} of the current basic block
+ * plus <tt>stackSize</tt>.
+ */
+private int stackSize;
+
+/**
+ * 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#inputStackTop beginStackSize} of the current basic
+ * block plus <tt>stackSize</tt>.
+ */
+private int maxStackSize;
+
+// ------------------------------------------------------------------------
+// Constructor
+// ------------------------------------------------------------------------
+
+/**
+ * Constructs a new {@link MethodWriter}.
+ *
+ * @param cw the class writer in which the method must be added.
+ * @param access the method's access flags (see {@link Opcodes}).
+ * @param name the method's name.
+ * @param desc the method's descriptor (see {@link Type}).
+ * @param signature the method's signature. May be <tt>null</tt>.
+ * @param exceptions the internal names of the method's exceptions. May be
+ * <tt>null</tt>.
+ * @param computeMaxs <tt>true</tt> if the maximum stack size and number
+ * of local variables must be automatically computed.
+ * @param computeFrames <tt>true</tt> if the stack map tables must be
+ * recomputed from scratch.
+ */
+MethodWriter(
+ final ClassWriter cw,
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions,
+ final boolean computeMaxs,
+ final boolean computeFrames){
+ if(cw.firstMethod == null)
+ {
+ cw.firstMethod = this;
+ }
+ else
+ {
+ cw.lastMethod.next = this;
+ }
+ cw.lastMethod = this;
+ this.cw = cw;
+ this.access = access;
+ this.name = cw.newUTF8(name);
+ this.desc = cw.newUTF8(desc);
+ this.descriptor = desc;
+ this.signature = signature;
+ 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.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
+ if(computeMaxs || computeFrames)
+ {
+ if(computeFrames && name.equals("<init>"))
+ {
+ this.access |= ACC_CONSTRUCTOR;
+ }
+ // updates maxLocals
+ int size = getArgumentsAndReturnSizes(descriptor) >> 2;
+ if((access & Opcodes.ACC_STATIC) != 0)
+ {
+ --size;
+ }
+ maxLocals = size;
+ // creates and visits the label for the first basic block
+ labels = new Label();
+ labels.status |= Label.PUSHED;
+ visitLabel(labels);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Implementation of the MethodVisitor interface
+// ------------------------------------------------------------------------
+
+public AnnotationVisitor visitAnnotationDefault(){
+ annd = new ByteVector();
+ return new AnnotationWriter(cw, false, annd, null, 0);
+}
+
+public AnnotationVisitor visitAnnotation(
+ final String desc,
+ final boolean visible){
+ ByteVector bv = new ByteVector();
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
+ if(visible)
+ {
+ aw.next = anns;
+ anns = aw;
+ }
+ else
+ {
+ aw.next = ianns;
+ ianns = aw;
+ }
+ return aw;
+}
+
+public AnnotationVisitor visitParameterAnnotation(
+ final int parameter,
+ final String desc,
+ final boolean visible){
+ ByteVector bv = new ByteVector();
+ // write type, and reserve space for values count
+ bv.putShort(cw.newUTF8(desc)).putShort(0);
+ AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
+ if(visible)
+ {
+ if(panns == null)
+ {
+ panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
+ }
+ aw.next = panns[parameter];
+ panns[parameter] = aw;
+ }
+ else
+ {
+ if(ipanns == null)
+ {
+ ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
+ }
+ aw.next = ipanns[parameter];
+ ipanns[parameter] = aw;
+ }
+ return aw;
+}
+
+public void visitAttribute(final Attribute attr){
+ if(attr.isCodeAttribute())
+ {
+ attr.next = cattrs;
+ cattrs = attr;
+ }
+ else
+ {
+ attr.next = attrs;
+ attrs = attr;
+ }
+}
+
+public void visitCode(){
+}
+
+public void visitFrame(
+ final int type,
+ final int nLocal,
+ final Object[] local,
+ final int nStack,
+ final Object[] stack){
+ if(compute == FRAMES)
+ {
+ return;
+ }
+
+ if(type == Opcodes.F_NEW)
+ {
+ startFrame(code.length, nLocal, nStack);
+ for(int i = 0; i < nLocal; ++i)
+ {
+ if(local[i] instanceof String)
+ {
+ frame[frameIndex++] = Frame.OBJECT
+ | cw.addType((String) local[i]);
+ }
+ else if(local[i] instanceof Integer)
+ {
+ frame[frameIndex++] = ((Integer) local[i]).intValue();
+ }
+ else
+ {
+ frame[frameIndex++] = Frame.UNINITIALIZED
+ | cw.addUninitializedType("",
+ ((Label) local[i]).position);
+ }
+ }
+ for(int i = 0; i < nStack; ++i)
+ {
+ if(stack[i] instanceof String)
+ {
+ frame[frameIndex++] = Frame.OBJECT
+ | cw.addType((String) stack[i]);
+ }
+ else if(stack[i] instanceof Integer)
+ {
+ frame[frameIndex++] = ((Integer) stack[i]).intValue();
+ }
+ else
+ {
+ frame[frameIndex++] = Frame.UNINITIALIZED
+ | cw.addUninitializedType("",
+ ((Label) stack[i]).position);
+ }
+ }
+ endFrame();
+ }
+ else
+ {
+ int delta;
+ if(stackMap == null)
+ {
+ stackMap = new ByteVector();
+ delta = code.length;
+ }
+ else
+ {
+ delta = code.length - previousFrameOffset - 1;
+ }
+
+ switch(type)
+ {
+ case Opcodes.F_FULL:
+ stackMap.putByte(FULL_FRAME)
+ .putShort(delta)
+ .putShort(nLocal);
+ for(int i = 0; i < nLocal; ++i)
+ {
+ writeFrameType(local[i]);
+ }
+ stackMap.putShort(nStack);
+ for(int i = 0; i < nStack; ++i)
+ {
+ writeFrameType(stack[i]);
+ }
+ break;
+ case Opcodes.F_APPEND:
+ stackMap.putByte(SAME_FRAME_EXTENDED + nLocal)
+ .putShort(delta);
+ for(int i = 0; i < nLocal; ++i)
+ {
+ writeFrameType(local[i]);
+ }
+ break;
+ case Opcodes.F_CHOP:
+ stackMap.putByte(SAME_FRAME_EXTENDED - nLocal)
+ .putShort(delta);
+ break;
+ case Opcodes.F_SAME:
+ if(delta < 64)
+ {
+ stackMap.putByte(delta);
+ }
+ else
+ {
+ stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
+ }
+ break;
+ case Opcodes.F_SAME1:
+ if(delta < 64)
+ {
+ stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
+ }
+ else
+ {
+ stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
+ .putShort(delta);
+ }
+ writeFrameType(stack[0]);
+ break;
+ }
+
+ previousFrameOffset = code.length;
+ ++frameCount;
+ }
+}
+
+public void visitInsn(final int opcode){
+ // adds the instruction to the bytecode of the method
+ code.putByte(opcode);
+ // update currentBlock
+ // Label currentBlock = this.currentBlock;
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(opcode, 0, null, null);
+ }
+ else
+ {
+ // updates current and max stack sizes
+ int size = stackSize + Frame.SIZE[opcode];
+ if(size > maxStackSize)
+ {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ // if opcode == ATHROW or xRETURN, ends current block (no successor)
+ if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
+ || opcode == Opcodes.ATHROW)
+ {
+ noSuccessor();
+ }
+ }
+}
+
+public void visitIntInsn(final int opcode, final int operand){
+ // Label currentBlock = this.currentBlock;
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(opcode, operand, null, null);
+ }
+ else if(opcode != Opcodes.NEWARRAY)
+ {
+ // updates current and max stack sizes only for 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 == Opcodes.SIPUSH)
+ {
+ code.put12(opcode, operand);
+ }
+ else
+ { // BIPUSH or NEWARRAY
+ code.put11(opcode, operand);
+ }
+}
+
+public void visitVarInsn(final int opcode, final int var){
+ // Label currentBlock = this.currentBlock;
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(opcode, var, null, null);
+ }
+ else
+ {
+ // updates current and max stack sizes
+ if(opcode == Opcodes.RET)
+ {
+ // no stack change, but end of current block (no successor)
+ currentBlock.status |= Label.RET;
+ // save 'stackSize' here for future use
+ // (see {@link #findSubroutineSuccessors})
+ currentBlock.inputStackTop = stackSize;
+ noSuccessor();
+ }
+ else
+ { // xLOAD or xSTORE
+ int size = stackSize + Frame.SIZE[opcode];
+ if(size > maxStackSize)
+ {
+ maxStackSize = size;
+ }
+ stackSize = size;
+ }
+ }
+ }
+ if(compute != NOTHING)
+ {
+ // updates max locals
+ int n;
+ if(opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
+ || opcode == Opcodes.LSTORE || opcode == Opcodes.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 != Opcodes.RET)
+ {
+ int opt;
+ if(opcode < Opcodes.ISTORE)
+ {
+ /* ILOAD_0 */
+ opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
+ }
+ else
+ {
+ /* ISTORE_0 */
+ opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
+ }
+ code.putByte(opt);
+ }
+ else if(var >= 256)
+ {
+ code.putByte(196 /* WIDE */).put12(opcode, var);
+ }
+ else
+ {
+ code.put11(opcode, var);
+ }
+ if(opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0)
+ {
+ visitLabel(new Label());
+ }
+}
+
+public void visitTypeInsn(final int opcode, final String desc){
+ Item i = cw.newClassItem(desc);
+ // Label currentBlock = this.currentBlock;
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(opcode, code.length, cw, i);
+ }
+ else if(opcode == Opcodes.NEW)
+ {
+ // updates current and max stack sizes only if opcode == NEW
+ // (no stack change 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, i.index);
+}
+
+public void visitFieldInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ Item i = cw.newFieldItem(owner, name, desc);
+ // Label currentBlock = this.currentBlock;
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(opcode, 0, cw, i);
+ }
+ else
+ {
+ int size;
+ // computes the stack size variation
+ char c = desc.charAt(0);
+ switch(opcode)
+ {
+ case Opcodes.GETSTATIC:
+ size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);
+ break;
+ case Opcodes.PUTSTATIC:
+ size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);
+ break;
+ case Opcodes.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, i.index);
+}
+
+public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String desc){
+ boolean itf = opcode == Opcodes.INVOKEINTERFACE;
+ Item i = cw.newMethodItem(owner, name, desc, itf);
+ int argSize = i.intVal;
+ // Label currentBlock = this.currentBlock;
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(opcode, 0, cw, i);
+ }
+ else
+ {
+ /*
+ * 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 == Opcodes.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(argSize == 0)
+ {
+ argSize = getArgumentsAndReturnSizes(desc);
+ i.intVal = argSize;
+ }
+ code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
+ }
+ else
+ {
+ code.put12(opcode, i.index);
+ }
+}
+
+public void visitJumpInsn(final int opcode, final Label label){
+ Label nextInsn = null;
+ // Label currentBlock = this.currentBlock;
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(opcode, 0, null, null);
+ // 'label' is the target of a jump instruction
+ label.getFirst().status |= Label.TARGET;
+ // adds 'label' as a successor of this basic block
+ addSuccessor(Edge.NORMAL, label);
+ if(opcode != Opcodes.GOTO)
+ {
+ // creates a Label for the next basic block
+ nextInsn = new Label();
+ }
+ }
+ else
+ {
+ if(opcode == Opcodes.JSR)
+ {
+ jsr = true;
+ currentBlock.status |= Label.JSR;
+ addSuccessor(stackSize + 1, label);
+ // creates a Label for the next basic block
+ nextInsn = new Label();
+ /*
+ * note that, by construction in this method, a JSR block
+ * has at least two successors in the control flow graph:
+ * the first one leads the next instruction after the JSR,
+ * while the second one leads to the JSR target.
+ */
+ }
+ else
+ {
+ // updates current stack size (max stack size unchanged
+ // because stack size variation always negative in this
+ // case)
+ stackSize += Frame.SIZE[opcode];
+ addSuccessor(stackSize, label);
+ }
+ }
+ }
+ // adds the instruction to the bytecode of the method
+ if((label.status & Label.RESOLVED) != 0
+ && 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 == Opcodes.GOTO)
+ {
+ code.putByte(200); // GOTO_W
+ }
+ else if(opcode == Opcodes.JSR)
+ {
+ code.putByte(201); // JSR_W
+ }
+ else
+ {
+ // if the IF instruction is transformed into IFNOT GOTO_W the
+ // next instruction becomes the target of the IFNOT instruction
+ if(nextInsn != null)
+ {
+ nextInsn.status |= Label.TARGET;
+ }
+ 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);
+ }
+ if(currentBlock != null)
+ {
+ if(nextInsn != null)
+ {
+ // if the jump instruction is not a GOTO, the next instruction
+ // is also a successor of this instruction. Calling visitLabel
+ // adds the label of this next instruction as a successor of the
+ // current block, and starts a new basic block
+ visitLabel(nextInsn);
+ }
+ if(opcode == Opcodes.GOTO)
+ {
+ noSuccessor();
+ }
+ }
+}
+
+public void visitLabel(final Label label){
+ // resolves previous forward references to label, if any
+ resize |= label.resolve(this, code.length, code.data);
+ // updates currentBlock
+ if((label.status & Label.DEBUG) != 0)
+ {
+ return;
+ }
+ if(compute == FRAMES)
+ {
+ if(currentBlock != null)
+ {
+ if(label.position == currentBlock.position)
+ {
+ // successive labels, do not start a new basic block
+ currentBlock.status |= (label.status & Label.TARGET);
+ label.frame = currentBlock.frame;
+ return;
+ }
+ // ends current block (with one new successor)
+ addSuccessor(Edge.NORMAL, label);
+ }
+ // begins a new current block
+ currentBlock = label;
+ if(label.frame == null)
+ {
+ label.frame = new Frame();
+ label.frame.owner = label;
+ }
+ // updates the basic block list
+ if(previousBlock != null)
+ {
+ if(label.position == previousBlock.position)
+ {
+ previousBlock.status |= (label.status & Label.TARGET);
+ label.frame = previousBlock.frame;
+ currentBlock = previousBlock;
+ return;
+ }
+ previousBlock.successor = label;
+ }
+ previousBlock = label;
+ }
+ else if(compute == MAXS)
+ {
+ if(currentBlock != null)
+ {
+ // ends current block (with one new successor)
+ currentBlock.outputStackMax = maxStackSize;
+ addSuccessor(stackSize, label);
+ }
+ // begins a new current block
+ currentBlock = label;
+ // resets the relative current and max stack sizes
+ stackSize = 0;
+ maxStackSize = 0;
+ // updates the basic block list
+ if(previousBlock != null)
+ {
+ previousBlock.successor = label;
+ }
+ previousBlock = label;
+ }
+}
+
+public void visitLdcInsn(final Object cst){
+ Item i = cw.newConstItem(cst);
+ // Label currentBlock = this.currentBlock;
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
+ }
+ else
+ {
+ 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(Opcodes.LDC, index);
+ }
+}
+
+public void visitIincInsn(final int var, final int increment){
+ if(currentBlock != null)
+ {
+ if(compute == FRAMES)
+ {
+ currentBlock.frame.execute(Opcodes.IINC, var, null, null);
+ }
+ }
+ if(compute != NOTHING)
+ {
+ // updates max locals
+ 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(Opcodes.IINC, var)
+ .putShort(increment);
+ }
+ else
+ {
+ code.putByte(Opcodes.IINC).put11(var, increment);
+ }
+}
+
+public void visitTableSwitchInsn(
+ final int min,
+ final int max,
+ final Label dflt,
+ final Label labels[]){
+ // adds the instruction to the bytecode of the method
+ int source = code.length;
+ code.putByte(Opcodes.TABLESWITCH);
+ code.length += (4 - code.length % 4) % 4;
+ 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);
+ }
|