/*** * ASM: a very small and fast Java bytecode manipulation framework * Copyright (c) 2000-2007 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 org.botnode.asm; /** * @author bbrown */ public class MethodWriter { /** * Pseudo access flag used to denote constructors. */ static final int ACC_CONSTRUCTOR = 262144; /** * Frame has exactly the same locals as the previous stack map frame and * number of stack items is zero. */ static final 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 */ static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) /** * Reserved for future use */ static final 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; */ static final 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. */ static final 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; */ static final 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. */ static final int APPEND_FRAME = 252; // to 254 // fc-fe /** * Full frame */ static final int FULL_FRAME = 255; // ff /** * Indicates that the maximum stack size and number of local variables must * be automatically computed. */ private static final int MAXS = 1; /** * 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. */ private static final int FRAMES = 0; private static final int NOTHING = 2; /** * The bytecode of this method. */ private ByteVector code = new ByteVector(); private int subroutines; private final String descriptor; private final int desc; private final int name; private int access; /** * Next method writer (see {@link ClassWriter#firstMethod firstMethod}). */ MethodWriter next; /** * The class writer to which this method must be added. */ final ClassWriter cw; private int maxStack; private int maxLocals; /** * Number of stack map frames in the StackMapTable attribute. */ private int frameCount; private int maxStackSize; private int stackSize; private final int compute; private boolean resize; /** * The StackMapTable attribute. */ private ByteVector stackMap; private int previousFrameOffset; private int[] previousFrame; 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; 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; //***************************************************** // Constructors and Methods //***************************************************** 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.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); if (computeMaxs || computeFrames) { if (computeFrames && "<init>".equals(name)) { 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); } } /** * Computes the size of the arguments and of the return value of a method. */ static int getArgumentsAndReturnSizes(final String desc) { int n = 1; int c = 1; while (true) { char car = desc.charAt(c++); if (car == ')') { car = desc.charAt(c); return n << 2 | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); } else if (car == 'L') { while (desc.charAt(c++) != ';') { } n += 1; } else if (car == '[') { while ((car = desc.charAt(c)) == '[') { ++c; } if (car == 'D' || car == 'J') { n -= 1; } } else if (car == 'D' || car == 'J') { n += 2; } else { n += 1; } } } /** * Returns the size of the bytecode of this method. * */ final int getSize() { int size = 8; if (code.length > 0) { cw.newUTF8("Code"); size += 18 + code.length + 8 * handlerCount; if (localVar != null) { cw.newUTF8("LocalVariableTable"); size += 8 + localVar.length; } if (localVarType != null) { cw.newUTF8("LocalVariableTypeTable"); size += 8 + localVarType.length; } if (stackMap != null) { boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; cw.newUTF8(zip ? "StackMapTable" : "StackMap"); size += 8 + stackMap.length; } if (cattrs != null) { size += cattrs.getSize(cw, code.data, code.length, maxStack, maxLocals); } } if (attrs != null) { size += attrs.getSize(cw, null, 0, -1, -1); } return size; } /** * Puts the bytecode of this method in the given byte vector. */ final void put(final ByteVector out) { out.putShort(access).putShort(name).putShort(desc); } /** * Writes a short value in the given byte array. */ static void writeShort(final byte[] b, final int index, final int s) { b[index] = (byte) (s >>> 8); b[index + 1] = (byte) s; } } // End of class