| /** |
| * 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.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| 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; |
| |
| /** |
| * StackMapAttribute is used by CDLC preverifier. Definition is given in |
| * appendix "CLDC Byte Code Typechecker Specification" from CDLC 1.1 |
| * specification. <p> <i>Note that this implementation does not calculate |
| * StackMapFrame structures from the method bytecode. If method code is changed |
| * or generated from scratch, then developer is responsible to prepare a correct |
| * StackMapFrame structures.</i> <p> The format of the stack map in the class |
| * file is given below. In the following, <ul> <li>if the length of the |
| * method's byte code1 is 65535 or less, then <tt>uoffset</tt> represents the |
| * type u2; otherwise <tt>uoffset</tt> represents the type u4.</li> <li>If |
| * the maximum number of local variables for the method is 65535 or less, then |
| * <tt>ulocalvar</tt> represents the type u2; otherwise <tt>ulocalvar</tt> |
| * represents the type u4.</li> <li>If the maximum size of the operand stack |
| * is 65535 or less, then <tt>ustack</tt> represents the type u2; otherwise |
| * ustack represents the type u4.</li> </ul> |
| * |
| * <pre> |
| * stack_map { // attribute StackMap |
| * u2 attribute_name_index; |
| * u4 attribute_length |
| * uoffset number_of_entries; |
| * stack_map_frame entries[number_of_entries]; |
| * } |
| * </pre> |
| * |
| * Each stack map frame has the following format: |
| * |
| * <pre> |
| * stack_map_frame { |
| * uoffset offset; |
| * ulocalvar number_of_locals; |
| * verification_type_info locals[number_of_locals]; |
| * ustack number_of_stack_items; |
| * verification_type_info stack[number_of_stack_items]; |
| * } |
| * </pre> |
| * |
| * The <tt>verification_type_info</tt> structure consists of a one-byte tag |
| * followed by zero or more bytes, giving more information about the tag. Each |
| * <tt>verification_type_info</tt> structure specifies the verification type |
| * of one or two locations. |
| * |
| * <pre> |
| * 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; |
| * } |
| * |
| * Top_variable_info { |
| * u1 tag = ITEM_Top; // 0 |
| * } |
| * |
| * Integer_variable_info { |
| * u1 tag = ITEM_Integer; // 1 |
| * } |
| * |
| * Float_variable_info { |
| * u1 tag = ITEM_Float; // 2 |
| * } |
| * |
| * Long_variable_info { |
| * u1 tag = ITEM_Long; // 4 |
| * } |
| * |
| * Double_variable_info { |
| * u1 tag = ITEM_Double; // 3 |
| * } |
| * |
| * Null_variable_info { |
| * u1 tag = ITEM_Null; // 5 |
| * } |
| * |
| * UninitializedThis_variable_info { |
| * u1 tag = ITEM_UninitializedThis; // 6 |
| * } |
| * |
| * Object_variable_info { |
| * u1 tag = ITEM_Object; // 7 |
| * u2 cpool_index; |
| * } |
| * |
| * Uninitialized_variable_info { |
| * u1 tag = ITEM_Uninitialized // 8 |
| * uoffset offset; |
| * } |
| * </pre> |
| * |
| * @see <a href="http://www.jcp.org/en/jsr/detail?id=139">JSR 139 : Connected |
| * Limited Device Configuration 1.1</a> |
| * |
| * @author Eugene Kuleshov |
| */ |
| public class StackMapAttribute extends Attribute { |
| |
| static final int MAX_SIZE = 65535; |
| |
| /** |
| * A List of <code>StackMapFrame</code> instances. |
| */ |
| public List frames = new ArrayList(); |
| |
| public StackMapAttribute() { |
| super("StackMap"); |
| } |
| |
| public StackMapAttribute(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) |
| { |
| StackMapAttribute attr = new StackMapAttribute(); |
| // note that this is not the size of Code attribute |
| boolean isExtCodeSize = cr.readInt(codeOff + 4) > MAX_SIZE; |
| boolean isExtLocals = cr.readUnsignedShort(codeOff + 2) > MAX_SIZE; |
| boolean isExtStack = cr.readUnsignedShort(codeOff) > MAX_SIZE; |
| |
| int size = 0; |
| if (isExtCodeSize) { |
| size = cr.readInt(off); |
| off += 4; |
| } else { |
| size = cr.readUnsignedShort(off); |
| off += 2; |
| } |
| for (int i = 0; i < size; i++) { |
| int offset; |
| if (isExtCodeSize) { |
| offset = cr.readInt(off); |
| off += 4; |
| } else { |
| offset = cr.readUnsignedShort(off); |
| off += 2; |
| } |
| |
| Label label = getLabel(offset, labels); |
| List locals = new ArrayList(); |
| List stack = new ArrayList(); |
| |
| off = readTypeInfo(cr, |
| off, |
| locals, |
| labels, |
| buf, |
| isExtLocals, |
| isExtCodeSize); |
| off = readTypeInfo(cr, |
| off, |
| stack, |
| labels, |
| buf, |
| isExtStack, |
| isExtCodeSize); |
| |
| attr.frames.add(new StackMapFrame(label, locals, stack)); |
| } |
| return attr; |
| } |
| |
| private int readTypeInfo( |
| ClassReader cr, |
| int off, |
| List info, |
| Label[] labels, |
| char[] buf, |
| boolean isExt, |
| boolean isExtCode) |
| { |
| int n = 0; |
| if (isExt) { |
| n = cr.readInt(off); |
| off += 4; |
| } else { |
| n = cr.readUnsignedShort(off); |
| off += 2; |
| } |
| for (int j = 0; j < n; j++) { |
| int itemType = cr.readByte(off++); |
| StackMapType typeInfo = StackMapType.getTypeInfo(itemType); |
| info.add(typeInfo); |
| switch (itemType) { |
| case StackMapType.ITEM_Object: // |
| typeInfo.setObject(cr.readClass(off, buf)); |
| off += 2; |
| break; |
| case StackMapType.ITEM_Uninitialized: // |
| int offset; |
| if (isExtCode) { |
| offset = cr.readInt(off); |
| off += 4; |
| } else { |
| offset = cr.readUnsignedShort(off); |
| off += 2; |
| } |
| typeInfo.setLabel(getLabel(offset, labels)); |
| break; |
| } |
| } |
| return off; |
| } |
| |
| private void writeTypeInfo(ByteVector bv, ClassWriter cw, List info, int max) |
| { |
| if (max > StackMapAttribute.MAX_SIZE) { |
| bv.putInt(info.size()); |
| } else { |
| bv.putShort(info.size()); |
| } |
| for (int j = 0; j < info.size(); 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; |
| |
| } |
| } |
| } |
| |
| private Label getLabel(int offset, Label[] labels) { |
| Label l = labels[offset]; |
| if (l != null) { |
| return l; |
| } |
| return labels[offset] = new Label(); |
| } |
| |
| protected ByteVector write( |
| ClassWriter cw, |
| byte[] code, |
| int len, |
| int maxStack, |
| int maxLocals) |
| { |
| ByteVector bv = new ByteVector(); |
| if (code != null && code.length > MAX_SIZE) { // TODO verify value |
| bv.putInt(frames.size()); |
| } else { |
| bv.putShort(frames.size()); |
| } |
| for (int i = 0; i < frames.size(); i++) { |
| writeFrame((StackMapFrame) frames.get(i), |
| cw, |
| maxStack, |
| maxLocals, |
| bv); |
| } |
| return bv; |
| } |
| |
| protected Label[] getLabels() { |
| HashSet labels = new HashSet(); |
| for (int i = 0; i < frames.size(); i++) { |
| getFrameLabels((StackMapFrame) frames.get(i), labels); |
| } |
| return (Label[]) labels.toArray(new Label[labels.size()]); |
| } |
| |
| private void writeFrame( |
| StackMapFrame frame, |
| ClassWriter cw, |
| int maxStack, |
| int maxLocals, |
| ByteVector bv) |
| { |
| bv.putShort(frame.label.getOffset()); |
| writeTypeInfo(bv, cw, frame.locals, maxLocals); |
| writeTypeInfo(bv, cw, frame.stack, maxStack); |
| } |
| |
| private void getFrameLabels(StackMapFrame frame, Set labels) { |
| labels.add(frame.label); |
| getTypeInfoLabels(labels, frame.locals); |
| getTypeInfoLabels(labels, frame.stack); |
| } |
| |
| private void getTypeInfoLabels(Set labels, List info) { |
| for (Iterator it = info.iterator(); it.hasNext();) { |
| StackMapType typeInfo = (StackMapType) it.next(); |
| if (typeInfo.getType() == StackMapType.ITEM_Uninitialized) { |
| labels.add(typeInfo.getLabel()); |
| } |
| } |
| } |
| |
| public String toString() { |
| StringBuffer sb = new StringBuffer("StackMap["); |
| for (int i = 0; i < frames.size(); i++) { |
| sb.append('\n').append('[').append(frames.get(i)).append(']'); |
| } |
| sb.append("\n]"); |
| return sb.toString(); |
| } |
| } |