You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
575 lines
17 KiB
575 lines
17 KiB
/* |
|
* 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.fr.third.javassist.bytecode; |
|
|
|
import java.io.ByteArrayOutputStream; |
|
import java.io.DataInputStream; |
|
import java.io.IOException; |
|
import java.util.Map; |
|
|
|
import com.fr.third.javassist.CannotCompileException; |
|
import com.fr.third.javassist.CtBehavior; |
|
import com.fr.third.javassist.CtClass; |
|
|
|
/** |
|
* 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 <code>"StackMap"</code>. |
|
*/ |
|
public static final String tag = "StackMap"; |
|
|
|
|
|
/** |
|
* 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 <code>number_of_entries</code>. |
|
*/ |
|
public int numOfEntries() { |
|
return ByteArray.readU16bit(info, 0); |
|
} |
|
|
|
/** |
|
* <code>Top_variable_info.tag</code>. |
|
*/ |
|
public static final int TOP = 0; |
|
|
|
/** |
|
* <code>Integer_variable_info.tag</code>. |
|
*/ |
|
public static final int INTEGER = 1; |
|
|
|
/** |
|
* <code>Float_variable_info.tag</code>. |
|
*/ |
|
public static final int FLOAT = 2; |
|
|
|
/** |
|
* <code>Double_variable_info.tag</code>. |
|
*/ |
|
public static final int DOUBLE = 3; |
|
|
|
/** |
|
* <code>Long_variable_info.tag</code>. |
|
*/ |
|
public static final int LONG = 4; |
|
|
|
/** |
|
* <code>Null_variable_info.tag</code>. |
|
*/ |
|
public static final int NULL = 5; |
|
|
|
/** |
|
* <code>UninitializedThis_variable_info.tag</code>. |
|
*/ |
|
public static final int THIS = 6; |
|
|
|
/** |
|
* <code>Object_variable_info.tag</code>. |
|
*/ |
|
public static final int OBJECT = 7; |
|
|
|
/** |
|
* <code>Uninitialized_variable_info.tag</code>. |
|
*/ |
|
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 <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 <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 <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 <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 <code>Object_variable_info</code> |
|
* is visited. |
|
*/ |
|
public void objectVariable(int pos, int clazz) {} |
|
|
|
/** |
|
* 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 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 <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 CtBehavior#addParameter(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. |
|
* |
|
* <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; |
|
} |
|
|
|
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 <code>StackMap</code> attribute. |
|
*/ |
|
public StackMap toStackMap(ConstPool cp) { |
|
return new StackMap(cp, output.toByteArray()); |
|
} |
|
|
|
/** |
|
* 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. |
|
*/ |
|
public void write16bit(int value) { |
|
output.write((value >>> 8) & 0xff); |
|
output.write(value & 0xff); |
|
} |
|
} |
|
}
|
|
|