/** * 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 org.objectweb.asm.attrs; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.objectweb.asm.Attribute; import org.objectweb.asm.ByteVector; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** * The stack map attribute is used during the process of verification by * typechecking (§4.11.1).

A stack map attribute consists of zero or * more stack map frames. Each stack map frame specifies (either explicitly or * implicitly) a bytecode offset, the verification types (§4.11.1) for the local * variables, and the verification types for the operand stack.

The * type checker deals with and manipulates the expected types of a method's * local variables and operand stack. Throughout this section, a location refers * to either a single local variable or to a single operand stack entry.
*
We will use the terms stack frame map and type state interchangeably to * describe a mapping from locations in the operand stack and local variables of * a method to verification types. We will usually use the term stack frame map * when such a mapping is provided in the class file, and the term type state * when the mapping is inferred by the type checker.

If a method's * Code attribute does not have a StackMapTable attribute, it has an implicit * stack map attribute. This implicit stack map attribute is equivalent to a * StackMapTable attribute with number_of_entries equal to zero. A method's Code * attribute may have at most one StackMapTable attribute, otherwise a * java.lang.ClassFormatError is thrown.

The format of the stack map * in the class file is given below. In the following, if the length of the * method's byte code is 65535 or less, then uoffset represents the type u2; * otherwise uoffset represents the type u4. If the maximum number of local * variables for the method is 65535 or less, then ulocalvar * represents the type u2; otherwise ulocalvar represents the type u4. If the * maximum size of the operand stack is 65535 or less, then ustack * represents the type u2; otherwise ustack represents the type u4. * *
 * stack_map { // attribute StackMapTable
 *   u2 attribute_name_index;
 *   u4 attribute_length
 *   uoffset number_of_entries;
 *   stack_map_frame entries[number_of_entries];
 * }
 * 
* * Each stack_map_frame structure specifies the type state at a particular byte * code offset. Each frame type specifies (explicitly or implicitly) a value, * offset_delta, that is used to calulate the actual byte code offset at which * it applies. The byte code offset at which the frame applies is given by * adding 1 + offset_delta to the offset of the * previous frame, unless the previous frame is the initial frame of the method, * in which case the byte code offset is offset_delta.

* Note that the length of the byte codes is not the same as the length of * the Code attribute. The byte codes are embedded in the Code attribute, along * with other information.

By using an offset delta rather than * the actual byte code offset we ensure, by definition, that stack map frames * are in the correctly sorted order. Furthermore, by consistently using the * formula offset_delta + 1 for all explicit frames, we guarantee * the absence of duplicates.

All frame types, even full_frame, rely * on the previous frame for some of their semantics. This raises the question * of what is the very first frame? The initial frame is implicit, and computed * from the method descriptor. See the Prolog code for methodInitialStacFrame. *

The stack_map_frame structure consists of a one-byte tag followed * by zero or more bytes, giving more information, depending upon the tag.
*
A stack map frame may belong to one of several frame types * *
 * union stack_map_frame {
 *   same_frame;
 *   same_locals_1_stack_item_frame;
 *   chop_frame;
 *   same_frame_extended;
 *   append_frame;
 *   full_frame;
 * }
 * 
* * The frame type same_frame is represented by tags in the range [0-63]. If the * frame type is same_frame, it means the frame has exactly the same locals as * the previous stack map frame and that the number of stack items is zero. The * offset_delta value for the frame is the value of the tag field, frame_type. * The form of such a frame is then: * *
 * same_frame {
 *   u1 frame_type = SAME;  // 0-63
 * }
 * 
* * The frame type same_locals_1_stack_item_frame is represented by tags in the * range [64, 127]. If the frame_type is same_locals_1_stack_item_frame, it * means the frame has exactly the same locals as the previous stack map frame * and that the number of stack items is 1. The offset_delta value for the frame * is the value (frame_type - 64). There is a verification_type_info following * the frame_type for the one stack item. The form of such a frame is then: * *
 * same_locals_1_stack_item_frame {
 *   u1 frame_type = SAME_LOCALS_1_STACK_ITEM;  // 64-127
 *    verification_type_info stack[1];
 * }
 * 
* * Tags in the range [128-247] are reserved for future use.

The frame * type chop_frame is represented by tags in the range [248-250]. If the * frame_type is chop_frame, it means that the 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.

The form of * such a frame is then: * *
 * chop_frame {
 *   u1 frame_type=CHOP;  // 248-250
 *   uoffset offset_delta;
 * }
 * 
* * The frame type same_frame_extended is represented by the tag value 251. If * the frame type is same_frame_extended, it means the frame has exactly the * same locals as the previous stack map frame and that the number of stack * items is zero. The form of such a frame is then: * *
 * same_frame_extended {
 *   u1 frame_type = SAME_FRAME_EXTENDED;  // 251
 *   uoffset offset_delta;
 * }
 * 
* * The frame type append_frame is represented by tags in the range [252-254]. If * the frame_type is append_frame, it means that the 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.

The * form of such a frame is then: * *
 * append_frame {
 *   u1 frame_type =APPEND;  // 252-254
 *   uoffset offset_delta;
 *   verification_type_info locals[frame_type -251];
 * }
 * 
* * The 0th entry in locals represents the type of the first additional local * variable. If locals[M] represents local variable N, then locals[M+1] * represents local variable N+1 if locals[M] is one of Top_variable_info, * Integer_variable_info, Float_variable_info, Null_variable_info, * UninitializedThis_variable_info, Object_variable_info, or * Uninitialized_variable_info, otherwise locals[M+1] represents local variable * N+2. It is an error if, for any index i, locals[i] represents a local * variable whose index is greater than the maximum number of local variables * for the method.

The frame type full_frame is represented by the tag * value 255. The form of such a frame is then: * *
 * full_frame {
 *   u1 frame_type = FULL_FRAME;  // 255
 *   uoffset offset_delta;
 *   ulocalvar number_of_locals;
 *   verification_type_info locals[number_of_locals];
 *   ustack number_of_stack_items;
 *   verification_type_info stack[number_of_stack_items];
 * }
 * 
* * The 0th entry in locals represents the type of local variable 0. If locals[M] * represents local variable N, then locals[M+1] represents local variable N+1 * if locals[M] is one of Top_variable_info, Integer_variable_info, * Float_variable_info, Null_variable_info, UninitializedThis_variable_info, * Object_variable_info, or Uninitialized_variable_info, otherwise locals[M+1] * represents local variable N+2. It is an error if, for any index i, locals[i] * represents a local variable whose index is greater than the maximum number of * local variables for the method.

The 0th entry in stack represents * the type of the bottom of the stack, and subsequent entries represent types * of stack elements closer to the top of the operand stack. We shall refer to * the bottom element of the stack as stack element 0, and to subsequent * elements as stack element 1, 2 etc. If stack[M] represents stack element N, * then stack[M+1] represents stack element N+1 if stack[M] is one of * Top_variable_info, Integer_variable_info, Float_variable_info, * Null_variable_info, UninitializedThis_variable_info, Object_variable_info, or * Uninitialized_variable_info, otherwise stack[M+1] represents stack element * N+2. It is an error if, for any index i, stack[i] represents a stack entry * whose index is greater than the maximum operand stack size for the method. *

We say that an instruction in the byte code has a corresponding * stack map frame if the offset in the offset field of the stack map frame is * the same as the offset of the instruction in the byte codes.

The * verification_type_info structure consists of a one-byte tag followed by zero * or more bytes, giving more information about the tag. Each * verification_type_info structure specifies the verification type of one or * two locations. * *
 * union verification_type_info {
 *   Top_variable_info;
 *   Integer_variable_info;
 *   Float_variable_info;
 *   Long_variable_info;
 *   Double_variable_info;
 *   Null_variable_info;
 *   UninitializedThis_variable_info;
 *   Object_variable_info;
 *   Uninitialized_variable_info;
 * }
 * 
* * The Top_variable_info type indicates that the local variable has the * verification type top (T.) * *
 * Top_variable_info {
 *   u1 tag = ITEM_Top; // 0
 * }
 * 
* * The Integer_variable_info type indicates that the location contains the * verification type int. * *
 * Integer_variable_info {
 *   u1 tag = ITEM_Integer; // 1
 * }
 * 
* * The Float_variable_info type indicates that the location contains the * verification type float. * *
 * Float_variable_info {
 *   u1 tag = ITEM_Float; // 2
 * }
 * 
* * The Long_variable_info type indicates that the location contains the * verification type long. If the location is a local variable, then: * * * * If the location is an operand stack entry, then: * * * * This structure gives the contents of two locations in the operand stack or in * the local variables. * *
 * Long_variable_info {
 *   u1 tag = ITEM_Long; // 4
 * }
 * 
* * The Double_variable_info type indicates that the location contains the * verification type double. If the location is a local variable, then: * * * * If the location is an operand stack entry, then: * * * * This structure gives the contents of two locations in in the operand stack or * in the local variables. * *
 * Double_variable_info {
 *   u1 tag = ITEM_Double; // 3
 * }
 * 
* * The Null_variable_info type indicates that location contains the verification * type null. * *
 * Null_variable_info {
 *   u1 tag = ITEM_Null; // 5
 * }
 * 
* * The UninitializedThis_variable_info type indicates that the location contains * the verification type uninitializedThis. * *
 * UninitializedThis_variable_info {
 *   u1 tag = ITEM_UninitializedThis; // 6
 * }
 * 
* * The Object_variable_info type indicates that the location contains an * instance of the class referenced by the constant pool entry. * *
 * Object_variable_info {
 *   u1 tag = ITEM_Object; // 7
 *   u2 cpool_index;
 * }
 * 
* * The Uninitialized_variable_info indicates that the location contains the * verification type uninitialized(offset). The offset item indicates the offset * of the new instruction that created the object being stored in the location. * *
 * Uninitialized_variable_info {
 *   u1 tag = ITEM_Uninitialized // 8
 *   uoffset offset;
 * }
 * 
* * @see "ClassFileFormat-Java6.fm Page 138 Friday, April 15, 2005 3:22 PM" * * @author Eugene Kuleshov */ public class StackMapTableAttribute extends Attribute { /** * Frame has exactly the same locals as the previous stack map frame and * number of stack items is zero. */ public 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 */ public static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 // (40-7f) /** * Reserved for future use */ public 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; */ public 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. */ public 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; */ public 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. */ public static final int APPEND_FRAME = 252; // to 254 // fc-fe /** * Full frame */ public static final int FULL_FRAME = 255; // ff private static final int MAX_SHORT = 65535; /** * A List of StackMapFrame instances. */ private List frames; public StackMapTableAttribute() { super("StackMapTable"); } public StackMapTableAttribute(List frames) { this(); this.frames = frames; } public List getFrames() { return frames; } public StackMapFrame getFrame(Label label) { for (int i = 0; i < frames.size(); i++) { StackMapFrame frame = (StackMapFrame) frames.get(i); if (frame.label == label) { return frame; } } return null; } public boolean isUnknown() { return false; } public boolean isCodeAttribute() { return true; } protected Attribute read( ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { ArrayList frames = new ArrayList(); // note that this is not the size of Code attribute boolean isExtCodeSize = cr.readInt(codeOff + 4) > MAX_SHORT; boolean isExtLocals = cr.readUnsignedShort(codeOff + 2) > MAX_SHORT; boolean isExtStack = cr.readUnsignedShort(codeOff) > MAX_SHORT; int offset = 0; int methodOff = getMethodOff(cr, codeOff, buf); StackMapFrame frame = new StackMapFrame(getLabel(offset, labels), calculateLocals(cr.readClass(cr.header + 2, buf), // owner cr.readUnsignedShort(methodOff), // method access cr.readUTF8(methodOff + 2, buf), // method name cr.readUTF8(methodOff + 4, buf)), // method desc Collections.EMPTY_LIST); frames.add(frame); // System.err.println( cr.readUTF8( methodOff + 2, buf)); // System.err.println( offset +" delta:" + 0 +" : "+ frame); int size; if (isExtCodeSize) { size = cr.readInt(off); off += 4; } else { size = cr.readUnsignedShort(off); off += 2; } for (; size > 0; size--) { int tag = cr.readByte(off); // & 0xff; off++; List stack; List locals; int offsetDelta; if (tag < SAME_LOCALS_1_STACK_ITEM_FRAME) { // SAME_FRAME offsetDelta = tag; locals = new ArrayList(frame.locals); stack = Collections.EMPTY_LIST; } else if (tag < RESERVED) { // SAME_LOCALS_1_STACK_ITEM_FRAME offsetDelta = tag - SAME_LOCALS_1_STACK_ITEM_FRAME; locals = new ArrayList(frame.locals); stack = new ArrayList(); // read verification_type_info stack[1]; off = readType(stack, isExtCodeSize, cr, off, labels, buf); } else { if (isExtCodeSize) { offsetDelta = cr.readInt(off); off += 4; } else { offsetDelta = cr.readUnsignedShort(off); off += 2; } if (tag == SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { // SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED locals = new ArrayList(frame.locals); stack = new ArrayList(); // read verification_type_info stack[1]; off = readType(stack, isExtCodeSize, cr, off, labels, buf); } else if (tag >= CHOP_FRAME && tag < SAME_FRAME_EXTENDED) { // CHOP_FRAME stack = Collections.EMPTY_LIST; int k = SAME_FRAME_EXTENDED - tag; // copy locals from prev frame and chop last k locals = new ArrayList(frame.locals.subList(0, frame.locals.size() - k)); } else if (tag == SAME_FRAME_EXTENDED) { // SAME_FRAME_EXTENDED stack = Collections.EMPTY_LIST; locals = new ArrayList(frame.locals); } else if ( /* tag>=APPEND && */tag < FULL_FRAME) { // APPEND_FRAME stack = Collections.EMPTY_LIST; // copy locals from prev frame and append new k locals = new ArrayList(frame.locals); for (int k = tag - SAME_FRAME_EXTENDED; k > 0; k--) { off = readType(locals, isExtCodeSize, cr, off, labels, buf); } } else if (tag == FULL_FRAME) { // FULL_FRAME // read verification_type_info locals[number_of_locals]; locals = new ArrayList(); off = readTypes(locals, isExtLocals, isExtCodeSize, cr, off, labels, buf); // read verification_type_info stack[number_of_stack_items]; stack = new ArrayList(); off = readTypes(stack, isExtStack, isExtCodeSize, cr, off, labels, buf); } else { throw new RuntimeException("Unknown frame type " + tag + " after offset " + offset); } } offset += offsetDelta; Label offsetLabel = getLabel(offset, labels); frame = new StackMapFrame(offsetLabel, locals, stack); frames.add(frame); // System.err.println( tag +" " + offset +" delta:" + offsetDelta + // " frameType:"+ frameType+" : "+ frame); offset++; } return new StackMapTableAttribute(frames); } protected ByteVector write( ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { ByteVector bv = new ByteVector(); // TODO verify this value (MAX_SHORT) boolean isExtCodeSize = code != null && code.length > MAX_SHORT; writeSize(frames.size() - 1, bv, isExtCodeSize); if (frames.size() < 2) { return bv; } boolean isExtLocals = maxLocals > MAX_SHORT; boolean isExtStack = maxStack > MAX_SHORT; // skip the first frame StackMapFrame frame = (StackMapFrame) frames.get(0); List locals = frame.locals; int offset = frame.label.getOffset(); for (int i = 1; i < frames.size(); i++) { frame = (StackMapFrame) frames.get(i); List clocals = frame.locals; List cstack = frame.stack; int coffset = frame.label.getOffset(); int clocalsSize = clocals.size(); int cstackSize = cstack.size(); int localsSize = locals.size(); int delta = coffset - offset; int type = FULL_FRAME; int k = 0; if (cstackSize == 0) { k = clocalsSize - localsSize; switch (k) { case -3: case -2: case -1: type = CHOP_FRAME; // CHOP or FULL localsSize = clocalsSize; // for full_frame check break; case 0: // SAME, SAME_EXTENDED or FULL type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; break; case 1: case 2: case 3: type = APPEND_FRAME; // APPEND or FULL break; } } else if (localsSize == clocalsSize && cstackSize == 1) { // SAME_LOCAL_1_STACK or FULL type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; } if (type != FULL_FRAME) { // verify if stack and locals are the same for (int j = 0; j < localsSize && type != FULL_FRAME; j++) { if (!locals.get(j).equals(clocals.get(j))) type = FULL_FRAME; } } switch (type) { case SAME_FRAME: bv.putByte(delta); break; case SAME_LOCALS_1_STACK_ITEM_FRAME: bv.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); writeTypeInfos(bv, cw, cstack, 0, 1); break; case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: bv.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED); writeSize(delta, bv, isExtCodeSize); writeTypeInfos(bv, cw, cstack, 0, 1); break; case SAME_FRAME_EXTENDED: bv.putByte(SAME_FRAME_EXTENDED); writeSize(delta, bv, isExtCodeSize); break; case CHOP_FRAME: bv.putByte(SAME_FRAME_EXTENDED + k); // negative k writeSize(delta, bv, isExtCodeSize); break; case APPEND_FRAME: bv.putByte(SAME_FRAME_EXTENDED + k); // positive k writeSize(delta, bv, isExtCodeSize); writeTypeInfos(bv, cw, clocals, clocalsSize - 1, clocalsSize); break; case FULL_FRAME: bv.putByte(FULL_FRAME); writeSize(delta, bv, isExtCodeSize); writeSize(clocalsSize, bv, isExtLocals); writeTypeInfos(bv, cw, clocals, 0, clocalsSize); writeSize(cstackSize, bv, isExtStack); writeTypeInfos(bv, cw, cstack, 0, cstackSize); break; default: throw new RuntimeException(); } offset = coffset + 1; // compensating non first offset locals = clocals; } return bv; } private void writeSize(int delta, ByteVector bv, boolean isExt) { if (isExt) { bv.putInt(delta); } else { bv.putShort(delta); } } private void writeTypeInfos( ByteVector bv, ClassWriter cw, List info, int start, int end) { for (int j = start; j < end; j++) { StackMapType typeInfo = (StackMapType) info.get(j); bv.putByte(typeInfo.getType()); switch (typeInfo.getType()) { case StackMapType.ITEM_Object: // bv.putShort(cw.newClass(typeInfo.getObject())); break; case StackMapType.ITEM_Uninitialized: // bv.putShort(typeInfo.getLabel().getOffset()); break; } } } public static int getMethodOff(ClassReader cr, int codeOff, char[] buf) { int off = cr.header + 6; int interfacesCount = cr.readUnsignedShort(off); off += 2 + interfacesCount * 2; int fieldsCount = cr.readUnsignedShort(off); off += 2; for (; fieldsCount > 0; --fieldsCount) { int attrCount = cr.readUnsignedShort(off + 6); // field attributes off += 8; for (; attrCount > 0; --attrCount) { off += 6 + cr.readInt(off + 2); } } int methodsCount = cr.readUnsignedShort(off); off += 2; for (; methodsCount > 0; --methodsCount) { int methodOff = off; int attrCount = cr.readUnsignedShort(off + 6); // method attributes off += 8; for (; attrCount > 0; --attrCount) { String attrName = cr.readUTF8(off, buf); off += 6; if (attrName.equals("Code")) { if (codeOff == off) { return methodOff; } } off += cr.readInt(off - 4); } } return -1; } /** * Use method signature and access flags to resolve initial locals state. * * @param className name of the method's owner class. * @param access access flags of the method. * @param methodName name of the method. * @param methodDesc descriptor of the method. * @return list of StackMapType instances representing locals * for an initial frame. */ public static List calculateLocals( String className, int access, String methodName, String methodDesc) { List locals = new ArrayList(); // TODO if ("".equals(methodName) && !className.equals("java/lang/Object")) { StackMapType typeInfo = StackMapType.getTypeInfo(StackMapType.ITEM_UninitializedThis); typeInfo.setObject(className); // this locals.add(typeInfo); } else if ((access & Opcodes.ACC_STATIC) == 0) { StackMapType typeInfo = StackMapType.getTypeInfo(StackMapType.ITEM_Object); typeInfo.setObject(className); // this locals.add(typeInfo); } Type[] types = Type.getArgumentTypes(methodDesc); for (int i = 0; i < types.length; i++) { Type t = types[i]; StackMapType smt; switch (t.getSort()) { case Type.LONG: smt = StackMapType.getTypeInfo(StackMapType.ITEM_Long); break; case Type.DOUBLE: smt = StackMapType.getTypeInfo(StackMapType.ITEM_Double); break; case Type.FLOAT: smt = StackMapType.getTypeInfo(StackMapType.ITEM_Float); break; case Type.ARRAY: case Type.OBJECT: smt = StackMapType.getTypeInfo(StackMapType.ITEM_Object); smt.setObject(t.getDescriptor()); // TODO verify name break; default: smt = StackMapType.getTypeInfo(StackMapType.ITEM_Integer); break; } } return locals; } private int readTypes( List info, boolean isExt, boolean isExtCodeSize, ClassReader cr, int off, Label[] labels, char[] buf) { int n = 0; if (isExt) { n = cr.readInt(off); off += 4; } else { n = cr.readUnsignedShort(off); off += 2; } for (; n > 0; n--) { off = readType(info, isExtCodeSize, cr, off, labels, buf); } return off; } private int readType( List info, boolean isExtCodeSize, ClassReader cr, int off, Label[] labels, char[] buf) { int itemType = cr.readByte(off++); StackMapType typeInfo = StackMapType.getTypeInfo(itemType); info.add(typeInfo); switch (itemType) { // case StackMapType.ITEM_Long: // // case StackMapType.ITEM_Double: // // info.add(StackMapType.getTypeInfo(StackMapType.ITEM_Top)); // break; case StackMapType.ITEM_Object: // typeInfo.setObject(cr.readClass(off, buf)); off += 2; break; case StackMapType.ITEM_Uninitialized: // int offset; if (isExtCodeSize) { offset = cr.readInt(off); off += 4; } else { offset = cr.readUnsignedShort(off); off += 2; } typeInfo.setLabel(getLabel(offset, labels)); break; } return off; } private Label getLabel(int offset, Label[] labels) { Label l = labels[offset]; if (l != null) { return l; } return labels[offset] = new Label(); } public String toString() { StringBuffer sb = new StringBuffer("StackMapTable["); for (int i = 0; i < frames.size(); i++) { sb.append('\n').append('[').append(frames.get(i)).append(']'); } sb.append("\n]"); return sb.toString(); } }