javassist.bytecode.StackMap Maven / Gradle / Ivy
/*
* 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;
import javassist.bytecode.StackMapTable.InsertLocal;
import javassist.bytecode.StackMapTable.NewRemover;
import javassist.bytecode.StackMapTable.Shifter;
/**
* 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 MethodInfo#doPreverify
* @see StackMapTable
* @since 3.12
*/
public class StackMap extends AttributeInfo {
/**
* The name of this attribute "StackMap"
.
*/
public static final String tag = "StackMap";
/**
* Constructs a stack_map
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
.
*/
public int numOfEntries() {
return ByteArray.readU16bit(info, 0);
}
/**
* Top_variable_info.tag
.
*/
public static final int TOP = 0;
/**
* Integer_variable_info.tag
.
*/
public static final int INTEGER = 1;
/**
* Float_variable_info.tag
.
*/
public static final int FLOAT = 2;
/**
* Double_variable_info.tag
.
*/
public static final int DOUBLE = 3;
/**
* Long_variable_info.tag
.
*/
public static final int LONG = 4;
/**
* Null_variable_info.tag
.
*/
public static final int NULL = 5;
/**
* UninitializedThis_variable_info.tag
.
*/
public static final int THIS = 6;
/**
* Object_variable_info.tag
.
*/
public static final int OBJECT = 7;
/**
* Uninitialized_variable_info.tag
.
*/
public static final int UNINIT = 8;
/**
* Makes a copy.
*/
public AttributeInfo copy(ConstPool newCp, Map classnames) {
Copier copier = new Copier(this, newCp, classnames);
copier.visit();
return copier.getStackMap();
}
/**
* A code walker for a StackMap attribute.
*/
public static class Walker {
byte[] info;
/**
* Constructs a walker.
*/
public Walker(StackMap sm) {
info = sm.get();
}
/**
* 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.
*/
public int locals(int pos, int offset, int num) {
return typeInfoArray(pos, offset, num, true);
}
/**
* Invoked when stack
of stack_map_frame
* 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.
*
* @param num the number of elements.
* @param isLocals true if this array is for locals
.
* false if it is for stack
.
*/
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.
*/
public void typeInfo(int pos, byte tag) {}
/**
* Invoked when an element of type Object_variable_info
* is visited.
*/
public void objectVariable(int pos, int clazz) {}
/**
* Invoked when an element of type Uninitialized_variable_info
* is visited.
*/
public void uninitialized(int pos, int offset) {}
}
static class Copier extends Walker {
byte[] dest;
ConstPool srcCp, destCp;
Map classnames;
Copier(StackMap map, ConstPool newCp, Map classnames) {
super(map);
srcCp = map.getConstPool();
dest = new byte[info.length];
destCp = newCp;
this.classnames = classnames;
}
public void visit() {
int num = ByteArray.readU16bit(info, 0);
ByteArray.write16bit(num, dest, 0);
super.visit();
}
public int locals(int pos, int offset, int num) {
ByteArray.write16bit(offset, dest, pos - 4);
return super.locals(pos, offset, num);
}
public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
ByteArray.write16bit(num, dest, pos - 2);
return super.typeInfoArray(pos, offset, num, isLocals);
}
public void typeInfo(int pos, byte tag) {
dest[pos] = tag;
}
public void objectVariable(int pos, int clazz) {
dest[pos] = OBJECT;
int newClazz = srcCp.copy(clazz, destCp, classnames);
ByteArray.write16bit(newClazz, dest, pos + 1);
}
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.
*
* @param index the index of the added local variable.
* @param tag the type tag of that local variable.
* It is available by StackMapTable.typeTagOf(char)
.
* @param 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 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();
}
public void visit() {
int num = ByteArray.readU16bit(info, 0);
writer.write16bit(num);
super.visit();
}
public int locals(int pos, int offset, int num) {
writer.write16bit(offset);
return super.locals(pos, offset, num);
}
public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
writer.write16bit(num);
return super.typeInfoArray(pos, offset, num, isLocals);
}
public void typeInfo(int pos, byte tag) {
writer.writeVerifyTypeInfo(tag, 0);
}
public void objectVariable(int pos, int clazz) {
writer.writeVerifyTypeInfo(OBJECT, clazz);
}
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;
}
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;
}
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);
}
public void uninitialized(int pos, int offset) {
if (where <= offset)
ByteArray.write16bit(offset + gap, info, pos + 1);
}
}
/**
* @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;
}
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.
*
* @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;
}
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.
*/
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();
}
public int locals(int pos, int offset, int num) {
writer.println(" * offset " + offset);
return super.locals(pos, offset, num);
}
}
/**
* Internal use only.
*/
public static class Writer {
// see javassist.bytecode.stackmap.MapMaker
private ByteArrayOutputStream output;
/**
* Constructs a writer.
*/
public Writer() {
output = new ByteArrayOutputStream();
}
/**
* Converts the written data into a byte array.
*/
public byte[] toByteArray() {
return output.toByteArray();
}
/**
* Converts to a StackMap
attribute.
*/
public StackMap toStackMap(ConstPool cp) {
return new StackMap(cp, output.toByteArray());
}
/**
* Writes a union verification_type_info
value.
*
* @param data cpool_index
or offset
.
*/
public void writeVerifyTypeInfo(int tag, int data) {
output.write(tag);
if (tag == StackMap.OBJECT || tag == StackMap.UNINIT)
write16bit(data);
}
/**
* Writes a 16bit value.
*/
public void write16bit(int value) {
output.write((value >>> 8) & 0xff);
output.write(value & 0xff);
}
}
}