com.feilong.lib.javassist.bytecode.StackMap Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* 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 com.feilong.lib.javassist.bytecode;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Map;
import com.feilong.lib.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 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.
*/
@Override
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;
}
@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.
*
* @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 com.feilong.lib.javassist.CtBehavior#addParameter(com.feilong.lib.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 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.
*
* @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.
*/
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.
*/
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);
}
}
}