/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package javassist.bytecode;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Map;

import javassist.CannotCompileException;

Another stack_map attribute defined in CLDC 1.1 for J2ME.

This is an entry in the attributes table of a Code attribute. It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification.

According to the CLDC specification, the sizes of some fields are not 16bit but 32bit if the code size is more than 64K or the number of the local variables is more than 64K. However, for the J2ME CLDC technology, they are always 16bit. The implementation of the StackMap class assumes they are 16bit.

See Also:
Since:3.12
/** * Another <code>stack_map</code> attribute defined in CLDC 1.1 for J2ME. * * <p>This is an entry in the attributes table of a Code attribute. * It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification. * * <p>According to the CLDC specification, the sizes of some fields are not 16bit * but 32bit if the code size is more than 64K or the number of the local variables * is more than 64K. However, for the J2ME CLDC technology, they are always 16bit. * The implementation of the StackMap class assumes they are 16bit. * * @see MethodInfo#doPreverify * @see StackMapTable * @since 3.12 */
public class StackMap extends AttributeInfo {
The name of this attribute "StackMap".
/** * The name of this attribute <code>"StackMap"</code>. */
public static final String tag = "StackMap";
Constructs a stack_map attribute.
/** * Constructs a <code>stack_map</code> attribute. */
StackMap(ConstPool cp, byte[] newInfo) { super(cp, tag, newInfo); } StackMap(ConstPool cp, int name_id, DataInputStream in) throws IOException { super(cp, name_id, in); }
Returns number_of_entries.
/** * Returns <code>number_of_entries</code>. */
public int numOfEntries() { return ByteArray.readU16bit(info, 0); }
Top_variable_info.tag.
/** * <code>Top_variable_info.tag</code>. */
public static final int TOP = 0;
Integer_variable_info.tag.
/** * <code>Integer_variable_info.tag</code>. */
public static final int INTEGER = 1;
Float_variable_info.tag.
/** * <code>Float_variable_info.tag</code>. */
public static final int FLOAT = 2;
Double_variable_info.tag.
/** * <code>Double_variable_info.tag</code>. */
public static final int DOUBLE = 3;
Long_variable_info.tag.
/** * <code>Long_variable_info.tag</code>. */
public static final int LONG = 4;
Null_variable_info.tag.
/** * <code>Null_variable_info.tag</code>. */
public static final int NULL = 5;
UninitializedThis_variable_info.tag.
/** * <code>UninitializedThis_variable_info.tag</code>. */
public static final int THIS = 6;
Object_variable_info.tag.
/** * <code>Object_variable_info.tag</code>. */
public static final int OBJECT = 7;
Uninitialized_variable_info.tag.
/** * <code>Uninitialized_variable_info.tag</code>. */
public static final int UNINIT = 8;
Makes a copy.
/** * Makes a copy. */
@Override public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) { Copier copier = new Copier(this, newCp, classnames); copier.visit(); return copier.getStackMap(); }
A code walker for a StackMap attribute.
/** * A code walker for a StackMap attribute. */
public static class Walker { byte[] info;
Constructs a walker.
/** * Constructs a walker. */
public Walker(StackMap sm) { info = sm.get(); }
Visits each entry of the stack map frames.
/** * Visits each entry of the stack map frames. */
public void visit() { int num = ByteArray.readU16bit(info, 0); int pos = 2; for (int i = 0; i < num; i++) { int offset = ByteArray.readU16bit(info, pos); int numLoc = ByteArray.readU16bit(info, pos + 2); pos = locals(pos + 4, offset, numLoc); int numStack = ByteArray.readU16bit(info, pos); pos = stack(pos + 2, offset, numStack); } }
Invoked when locals of stack_map_frame is visited.
/** * Invoked when <code>locals</code> of <code>stack_map_frame</code> * is visited. */
public int locals(int pos, int offset, int num) { return typeInfoArray(pos, offset, num, true); }
Invoked when stack of stack_map_frame is visited.
/** * Invoked when <code>stack</code> of <code>stack_map_frame</code> * is visited. */
public int stack(int pos, int offset, int num) { return typeInfoArray(pos, offset, num, false); }
Invoked when an array of verification_type_info is visited.
Params:
  • num – the number of elements.
  • isLocals – true if this array is for locals. false if it is for stack.
/** * Invoked when an array of <code>verification_type_info</code> is * visited. * * @param num the number of elements. * @param isLocals true if this array is for <code>locals</code>. * false if it is for <code>stack</code>. */
public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { for (int k = 0; k < num; k++) pos = typeInfoArray2(k, pos); return pos; } int typeInfoArray2(int k, int pos) { byte tag = info[pos]; if (tag == OBJECT) { int clazz = ByteArray.readU16bit(info, pos + 1); objectVariable(pos, clazz); pos += 3; } else if (tag == UNINIT) { int offsetOfNew = ByteArray.readU16bit(info, pos + 1); uninitialized(pos, offsetOfNew); pos += 3; } else { typeInfo(pos, tag); pos++; } return pos; }
Invoked when an element of verification_type_info (except Object_variable_info and Uninitialized_variable_info) is visited.
/** * Invoked when an element of <code>verification_type_info</code> * (except <code>Object_variable_info</code> and * <code>Uninitialized_variable_info</code>) is visited. */
public void typeInfo(int pos, byte tag) {}
Invoked when an element of type Object_variable_info is visited.
/** * Invoked when an element of type <code>Object_variable_info</code> * is visited. */
public void objectVariable(int pos, int clazz) {}
Invoked when an element of type Uninitialized_variable_info is visited.
/** * Invoked when an element of type <code>Uninitialized_variable_info</code> * is visited. */
public void uninitialized(int pos, int offset) {} } static class Copier extends Walker { byte[] dest; ConstPool srcCp, destCp; Map<String,String> classnames; Copier(StackMap map, ConstPool newCp, Map<String,String> classnames) { super(map); srcCp = map.getConstPool(); dest = new byte[info.length]; destCp = newCp; this.classnames = classnames; } @Override public void visit() { int num = ByteArray.readU16bit(info, 0); ByteArray.write16bit(num, dest, 0); super.visit(); } @Override public int locals(int pos, int offset, int num) { ByteArray.write16bit(offset, dest, pos - 4); return super.locals(pos, offset, num); } @Override public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { ByteArray.write16bit(num, dest, pos - 2); return super.typeInfoArray(pos, offset, num, isLocals); } @Override public void typeInfo(int pos, byte tag) { dest[pos] = tag; } @Override public void objectVariable(int pos, int clazz) { dest[pos] = OBJECT; int newClazz = srcCp.copy(clazz, destCp, classnames); ByteArray.write16bit(newClazz, dest, pos + 1); } @Override public void uninitialized(int pos, int offset) { dest[pos] = UNINIT; ByteArray.write16bit(offset, dest, pos + 1); } public StackMap getStackMap() { return new StackMap(destCp, dest); } }
Updates this stack map table when a new local variable is inserted for a new parameter.
Params:
  • index – the index of the added local variable.
  • tag – the type tag of that local variable. It is available by StackMapTable.typeTagOf(char).
  • classInfo – the index of the CONSTANT_Class_info structure in a constant pool table. This should be zero unless the tag is ITEM_Object.
See Also:
/** * Updates this stack map table when a new local variable is inserted * for a new parameter. * * @param index the index of the added local variable. * @param tag the type tag of that local variable. * It is available by <code>StackMapTable.typeTagOf(char)</code>. * @param classInfo the index of the <code>CONSTANT_Class_info</code> structure * in a constant pool table. This should be zero unless the tag * is <code>ITEM_Object</code>. * * @see javassist.CtBehavior#addParameter(javassist.CtClass) * @see StackMapTable#typeTagOf(char) * @see ConstPool */
public void insertLocal(int index, int tag, int classInfo) throws BadBytecode { byte[] data = new InsertLocal(this, index, tag, classInfo).doit(); this.set(data); } static class SimpleCopy extends Walker { Writer writer; SimpleCopy(StackMap map) { super(map); writer = new Writer(); } byte[] doit() { visit(); return writer.toByteArray(); } @Override public void visit() { int num = ByteArray.readU16bit(info, 0); writer.write16bit(num); super.visit(); } @Override public int locals(int pos, int offset, int num) { writer.write16bit(offset); return super.locals(pos, offset, num); } @Override public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { writer.write16bit(num); return super.typeInfoArray(pos, offset, num, isLocals); } @Override public void typeInfo(int pos, byte tag) { writer.writeVerifyTypeInfo(tag, 0); } @Override public void objectVariable(int pos, int clazz) { writer.writeVerifyTypeInfo(OBJECT, clazz); } @Override public void uninitialized(int pos, int offset) { writer.writeVerifyTypeInfo(UNINIT, offset); } } static class InsertLocal extends SimpleCopy { private int varIndex; private int varTag, varData; InsertLocal(StackMap map, int varIndex, int varTag, int varData) { super(map); this.varIndex = varIndex; this.varTag = varTag; this.varData = varData; } @Override public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { if (!isLocals || num < varIndex) return super.typeInfoArray(pos, offset, num, isLocals); writer.write16bit(num + 1); for (int k = 0; k < num; k++) { if (k == varIndex) writeVarTypeInfo(); pos = typeInfoArray2(k, pos); } if (num == varIndex) writeVarTypeInfo(); return pos; } private void writeVarTypeInfo() { if (varTag == OBJECT) writer.writeVerifyTypeInfo(OBJECT, varData); else if (varTag == UNINIT) writer.writeVerifyTypeInfo(UNINIT, varData); else writer.writeVerifyTypeInfo(varTag, 0); } } void shiftPc(int where, int gapSize, boolean exclusive) throws BadBytecode { new Shifter(this, where, gapSize, exclusive).visit(); } static class Shifter extends Walker { private int where, gap; private boolean exclusive; public Shifter(StackMap smt, int where, int gap, boolean exclusive) { super(smt); this.where = where; this.gap = gap; this.exclusive = exclusive; } @Override public int locals(int pos, int offset, int num) { if (exclusive ? where <= offset : where < offset) ByteArray.write16bit(offset + gap, info, pos - 4); return super.locals(pos, offset, num); } @Override public void uninitialized(int pos, int offset) { if (where <= offset) ByteArray.write16bit(offset + gap, info, pos + 1); } }
See Also:
  • adjustOffsets.adjustOffsets(int, int)
/** * @see CodeIterator.Switcher#adjustOffsets(int, int) */
void shiftForSwitch(int where, int gapSize) throws BadBytecode { new SwitchShifter(this, where, gapSize).visit(); } static class SwitchShifter extends Walker { private int where, gap; public SwitchShifter(StackMap smt, int where, int gap) { super(smt); this.where = where; this.gap = gap; } @Override public int locals(int pos, int offset, int num) { if (where == pos + offset) ByteArray.write16bit(offset - gap, info, pos - 4); else if (where == pos) ByteArray.write16bit(offset + gap, info, pos - 4); return super.locals(pos, offset, num); } }
Undocumented method. Do not use; internal-use only.

This method is for javassist.convert.TransformNew. It is called to update the stack map when the NEW opcode (and the following DUP) is removed.

Params:
  • where – the position of the removed NEW opcode.
/** * Undocumented method. Do not use; internal-use only. * * <p>This method is for javassist.convert.TransformNew. * It is called to update the stack map when * the NEW opcode (and the following DUP) is removed. * * @param where the position of the removed NEW opcode. */
public void removeNew(int where) throws CannotCompileException { byte[] data = new NewRemover(this, where).doit(); this.set(data); } static class NewRemover extends SimpleCopy { int posOfNew; NewRemover(StackMap map, int where) { super(map); posOfNew = where; } @Override public int stack(int pos, int offset, int num) { return stackTypeInfoArray(pos, offset, num); } private int stackTypeInfoArray(int pos, int offset, int num) { int p = pos; int count = 0; for (int k = 0; k < num; k++) { byte tag = info[p]; if (tag == OBJECT) p += 3; else if (tag == UNINIT) { int offsetOfNew = ByteArray.readU16bit(info, p + 1); if (offsetOfNew == posOfNew) count++; p += 3; } else p++; } writer.write16bit(num - count); for (int k = 0; k < num; k++) { byte tag = info[pos]; if (tag == OBJECT) { int clazz = ByteArray.readU16bit(info, pos + 1); objectVariable(pos, clazz); pos += 3; } else if (tag == UNINIT) { int offsetOfNew = ByteArray.readU16bit(info, pos + 1); if (offsetOfNew != posOfNew) uninitialized(pos, offsetOfNew); pos += 3; } else { typeInfo(pos, tag); pos++; } } return pos; } }
Prints this stack map.
/** * Prints this stack map. */
public void print(java.io.PrintWriter out) { new Printer(this, out).print(); } static class Printer extends Walker { private java.io.PrintWriter writer; public Printer(StackMap map, java.io.PrintWriter out) { super(map); writer = out; } public void print() { int num = ByteArray.readU16bit(info, 0); writer.println(num + " entries"); visit(); } @Override public int locals(int pos, int offset, int num) { writer.println(" * offset " + offset); return super.locals(pos, offset, num); } }
Internal use only.
/** * Internal use only. */
public static class Writer { // see javassist.bytecode.stackmap.MapMaker private ByteArrayOutputStream output;
Constructs a writer.
/** * Constructs a writer. */
public Writer() { output = new ByteArrayOutputStream(); }
Converts the written data into a byte array.
/** * Converts the written data into a byte array. */
public byte[] toByteArray() { return output.toByteArray(); }
Converts to a StackMap attribute.
/** * Converts to a <code>StackMap</code> attribute. */
public StackMap toStackMap(ConstPool cp) { return new StackMap(cp, output.toByteArray()); }
Writes a union verification_type_info value.
Params:
  • data – cpool_index or offset.
/** * Writes a <code>union verification_type_info</code> value. * * @param data <code>cpool_index</code> or <code>offset</code>. */
public void writeVerifyTypeInfo(int tag, int data) { output.write(tag); if (tag == StackMap.OBJECT || tag == StackMap.UNINIT) write16bit(data); }
Writes a 16bit value.
/** * Writes a 16bit value. */
public void write16bit(int value) { output.write((value >>> 8) & 0xff); output.write(value & 0xff); } } }