git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@669 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
* or a static constructor (class initializer). | * or a static constructor (class initializer). | ||||
* It is the abstract super class of | * It is the abstract super class of | ||||
* <code>CtMethod</code> and <code>CtConstructor</code>. | * <code>CtMethod</code> and <code>CtConstructor</code>. | ||||
* | |||||
* <p>To directly read or modify bytecode, obtain <code>MethodInfo</code> | |||||
* objects. | |||||
* | |||||
* @see #getMethodInfo() | |||||
*/ | */ | ||||
public abstract class CtBehavior extends CtMember { | public abstract class CtBehavior extends CtMember { | ||||
protected MethodInfo methodInfo; | protected MethodInfo methodInfo; |
if (nameStr.charAt(0) < 'L') { | if (nameStr.charAt(0) < 'L') { | ||||
if (nameStr.equals(AnnotationDefaultAttribute.tag)) | if (nameStr.equals(AnnotationDefaultAttribute.tag)) | ||||
return new AnnotationDefaultAttribute(cp, name, in); | return new AnnotationDefaultAttribute(cp, name, in); | ||||
else if (nameStr.equals(BootstrapMethodsAttribute.tag)) | |||||
return new BootstrapMethodsAttribute(cp, name, in); | |||||
else if (nameStr.equals(CodeAttribute.tag)) | else if (nameStr.equals(CodeAttribute.tag)) | ||||
return new CodeAttribute(cp, name, in); | return new CodeAttribute(cp, name, in); | ||||
else if (nameStr.equals(ConstantAttribute.tag)) | else if (nameStr.equals(ConstantAttribute.tag)) |
package javassist.bytecode; | |||||
import java.io.DataInputStream; | |||||
import java.io.IOException; | |||||
import java.util.Map; | |||||
public class BootstrapMethodsAttribute extends AttributeInfo { | |||||
/** | |||||
* The name of this attribute <code>"BootstrapMethods"</code>. | |||||
*/ | |||||
public static final String tag = "BootstrapMethods"; | |||||
/** | |||||
* An element of <code>bootstrap_methods</code>. | |||||
*/ | |||||
public static class BootstrapMethod { | |||||
/** | |||||
* Constructs an element of <code>bootstrap_methods</code>. | |||||
* | |||||
* @param method <code>bootstrap_method_ref</code>. | |||||
* @param args <code>bootstrap_arguments</code>. | |||||
*/ | |||||
public BootstrapMethod(int method, int[] args) { | |||||
methodRef = method; | |||||
arguments = args; | |||||
} | |||||
/** | |||||
* <code>bootstrap_method_ref</code>. | |||||
* The value at this index must be a <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public int methodRef; | |||||
/** | |||||
* <code>bootstrap_arguments</code>. | |||||
*/ | |||||
public int[] arguments; | |||||
} | |||||
BootstrapMethodsAttribute(ConstPool cp, int n, DataInputStream in) | |||||
throws IOException | |||||
{ | |||||
super(cp, n, in); | |||||
} | |||||
/** | |||||
* Constructs a BootstrapMethods attribute. | |||||
* | |||||
* @param cp a constant pool table. | |||||
* @param methods the contents. | |||||
*/ | |||||
public BootstrapMethodsAttribute(ConstPool cp, BootstrapMethod[] methods) { | |||||
super(cp, tag); | |||||
int size = 2; | |||||
for (int i = 0; i < methods.length; i++) | |||||
size += 4 + methods[i].arguments.length * 2; | |||||
byte[] data = new byte[size]; | |||||
ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods | |||||
int pos = 2; | |||||
for (int i = 0; i < methods.length; i++) { | |||||
ByteArray.write16bit(methods[i].methodRef, data, pos); | |||||
ByteArray.write16bit(methods[i].arguments.length, data, pos + 2); | |||||
int[] args = methods[i].arguments; | |||||
pos += 4; | |||||
for (int k = 0; k < args.length; k++) { | |||||
ByteArray.write16bit(args[k], data, pos); | |||||
pos += 2; | |||||
} | |||||
} | |||||
set(data); | |||||
} | |||||
/** | |||||
* Obtains <code>bootstrap_methods</code> in this attribute. | |||||
* | |||||
* @return an array of <code>BootstrapMethod</code>. Since it | |||||
* is a fresh copy, modifying the returned array does not | |||||
* affect the original contents of this attribute. | |||||
*/ | |||||
public BootstrapMethod[] getMethods() { | |||||
byte[] data = this.get(); | |||||
int num = ByteArray.readU16bit(data, 0); | |||||
BootstrapMethod[] methods = new BootstrapMethod[num]; | |||||
int pos = 2; | |||||
for (int i = 0; i < num; i++) { | |||||
int ref = ByteArray.readU16bit(data, pos); | |||||
int len = ByteArray.readU16bit(data, pos + 2); | |||||
int[] args = new int[len]; | |||||
pos += 4; | |||||
for (int k = 0; k < len; k++) { | |||||
args[k] = ByteArray.readU16bit(data, pos); | |||||
pos += 2; | |||||
} | |||||
methods[i] = new BootstrapMethod(ref, args); | |||||
} | |||||
return methods; | |||||
} | |||||
/** | |||||
* Makes a copy. Class names are replaced according to the | |||||
* given <code>Map</code> object. | |||||
* | |||||
* @param newCp the constant pool table used by the new copy. | |||||
* @param classnames pairs of replaced and substituted | |||||
* class names. | |||||
*/ | |||||
public AttributeInfo copy(ConstPool newCp, Map classnames) { | |||||
BootstrapMethod[] methods = getMethods(); | |||||
ConstPool thisCp = getConstPool(); | |||||
for (int i = 0; i < methods.length; i++) { | |||||
BootstrapMethod m = methods[i]; | |||||
m.methodRef = thisCp.copy(m.methodRef, newCp, classnames); | |||||
for (int k = 0; k < m.arguments.length; k++) | |||||
m.arguments[k] = thisCp.copy(m.arguments[k], newCp, classnames); | |||||
} | |||||
return new BootstrapMethodsAttribute(newCp, methods); | |||||
} | |||||
} |
public void addInvokevirtual(int clazz, String name, String desc) { | public void addInvokevirtual(int clazz, String name, String desc) { | ||||
add(INVOKEVIRTUAL); | add(INVOKEVIRTUAL); | ||||
addIndex(constPool.addMethodrefInfo(clazz, name, desc)); | addIndex(constPool.addMethodrefInfo(clazz, name, desc)); | ||||
growStack(Descriptor.dataSize(desc) - 1); | |||||
growStack(Descriptor.dataSize(desc)); // assume CosntPool#REF_invokeStatic | |||||
} | } | ||||
/** | /** | ||||
growStack(Descriptor.dataSize(desc) - 1); | growStack(Descriptor.dataSize(desc) - 1); | ||||
} | } | ||||
/** | |||||
* Appends INVOKEDYNAMIC. | |||||
* | |||||
* @param bootstrap an index into the <code>bootstrap_methods</code> array | |||||
* of the bootstrap method table. | |||||
* @param name the method name. | |||||
* @param desc the method descriptor. | |||||
* @see Descriptor#ofMethod(CtClass,CtClass[]) | |||||
* @since 3.17 | |||||
*/ | |||||
public void addInvokedynamic(int bootstrap, String name, String desc) { | |||||
int nt = constPool.addNameAndTypeInfo(name, desc); | |||||
int dyn = constPool.addInvokeDynamicInfo(bootstrap, nt); | |||||
add(INVOKEDYNAMIC); | |||||
addIndex(dyn); | |||||
add(0, 0); | |||||
growStack(Descriptor.dataSize(desc) - 1); | |||||
} | |||||
/** | /** | ||||
* Appends LDC or LDC_W. The pushed item is a <code>String</code> | * Appends LDC or LDC_W. The pushed item is a <code>String</code> | ||||
* object. | * object. |
ci.u16bitAt(index + 1)); | ci.u16bitAt(index + 1)); | ||||
stack += Descriptor.dataSize(desc) - 1; | stack += Descriptor.dataSize(desc) - 1; | ||||
break; | break; | ||||
case INVOKEDYNAMIC : | |||||
desc = constPool.getInvokeDynamicType(ci.u16bitAt(index + 1)); | |||||
stack += Descriptor.dataSize(desc); // assume CosntPool#REF_invokeStatic | |||||
break; | |||||
case ATHROW : | case ATHROW : | ||||
stack = 1; // the stack becomes empty (1 means no values). | stack = 1; // the stack becomes empty (1 means no values). | ||||
break; | break; |
* use <code>CodeIterator</code>. | * use <code>CodeIterator</code>. | ||||
* | * | ||||
* @see CodeIterator | * @see CodeIterator | ||||
* @see #iterator() | |||||
*/ | */ | ||||
public class CodeAttribute extends AttributeInfo implements Opcode { | public class CodeAttribute extends AttributeInfo implements Opcode { | ||||
/** | /** | ||||
newcode[i + 3] = code[i + 3]; | newcode[i + 3] = code[i + 3]; | ||||
newcode[i + 4] = code[i + 4]; | newcode[i + 4] = code[i + 4]; | ||||
break; | break; | ||||
case INVOKEDYNAMIC : | |||||
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, | |||||
classnameMap); | |||||
newcode[i + 3] = 0; | |||||
newcode[i + 4] = 0; | |||||
break; | |||||
case MULTIANEWARRAY : | case MULTIANEWARRAY : | ||||
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, | copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, | ||||
classnameMap); | classnameMap); |
/** | /** | ||||
* An iterator for editing a code attribute. | * An iterator for editing a code attribute. | ||||
* | * | ||||
* <p>To directly read or edit a bytecode sequence, call {@link #byteAt(int)}, {@link #s16bitAt(int)}, | |||||
* {@link #writeByte(int, int)}, {@link #write16bit(int, int)}, and other methods. | |||||
* For example, if <code>method</code> refers to a <code>CtMethod</code> object, | |||||
* the following code substitutes the <code>NOP</code> instruction for the first | |||||
* instruction of the method: | |||||
* | |||||
* <pre>CodeAttribute ca = method.getMethodInfo().getCodeAttribute(); | |||||
* CodeIterator ci = ca.iterator(); | |||||
* ci.writeByte(Opcode.NOP, 0);</pre> | |||||
* | |||||
* <p>If there are multiple <code>CodeIterator</code>s referring to the | * <p>If there are multiple <code>CodeIterator</code>s referring to the | ||||
* same <code>Code_attribute</code>, then inserting a gap by one | * same <code>Code_attribute</code>, then inserting a gap by one | ||||
* <code>CodeIterator</code> will break the other | * <code>CodeIterator</code> will break the other | ||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, | ||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, | ||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, | 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, | ||||
// 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, | |||||
3, 3, 3, 3, 3, 5, 0, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, | |||||
3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, | |||||
5, 5 | 5, 5 | ||||
}; | }; | ||||
// 0 .. UNUSED (186), LOOKUPSWITCH, TABLESWITCH, WIDE | |||||
// 0 .. LOOKUPSWITCH, TABLESWITCH, WIDE | |||||
/** | /** | ||||
* Calculates the index of the next opcode. | * Calculates the index of the next opcode. | ||||
int padding = 3 - (pos & 3); | int padding = 3 - (pos & 3); | ||||
int nops = gap - padding; | int nops = gap - padding; | ||||
int bytecodeSize = 5 + (3 - (orgPos & 3)) + tableSize(); | int bytecodeSize = 5 + (3 - (orgPos & 3)) + tableSize(); | ||||
adjustOffsets(bytecodeSize, nops); | |||||
if (nops > 0) | |||||
adjustOffsets(bytecodeSize, nops); | |||||
newcode[dest++] = code[src]; | newcode[dest++] = code[src]; | ||||
while (padding-- > 0) | while (padding-- > 0) | ||||
newcode[dest++] = 0; | newcode[dest++] = 0; |
*/ | */ | ||||
public static final int CONST_Utf8 = Utf8Info.tag; | public static final int CONST_Utf8 = Utf8Info.tag; | ||||
/** | |||||
* <code>Cosnt_MethodHandle</code> | |||||
*/ | |||||
public static final int CONST_MethodHandle = MethodHandleInfo.tag; | |||||
/** | /** | ||||
* Represents the class using this constant pool table. | * Represents the class using this constant pool table. | ||||
*/ | */ | ||||
public static final CtClass THIS = null; | public static final CtClass THIS = null; | ||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_getField = 1; | |||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_getStatic = 2; | |||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_putField = 3; | |||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_putStatic = 4; | |||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_invokeVirtual = 5; | |||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_invokeStatic = 6; | |||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_invokeSpecial = 7; | |||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_newInvokeSpecial = 8; | |||||
/** | |||||
* <code>reference_kind</code> of <code>CONSTANT_MethodHandle_info</code>. | |||||
*/ | |||||
public static final int REF_invokeInterface = 9; | |||||
/** | /** | ||||
* Constructs a constant pool table. | * Constructs a constant pool table. | ||||
* | * | ||||
return utf.string; | return utf.string; | ||||
} | } | ||||
/** | |||||
* Reads the <code>reference_kind</code> field of the | |||||
* <code>CONSTANT_MethodHandle_info</code> structure | |||||
* at the given index. | |||||
* | |||||
* @see #REF_getField | |||||
* @see #REF_getStatic | |||||
* @see #REF_invokeInterface | |||||
* @see #REF_invokeSpecial | |||||
* @see #REF_invokeStatic | |||||
* @see #REF_invokeVirtual | |||||
* @see #REF_newInvokeSpecial | |||||
* @see #REF_putField | |||||
* @see #REF_putStatic | |||||
* @since 3.17 | |||||
*/ | |||||
public int getMethodHandleKind(int index) { | |||||
MethodHandleInfo mhinfo = (MethodHandleInfo)getItem(index); | |||||
return mhinfo.refKind; | |||||
} | |||||
/** | |||||
* Reads the <code>reference_index</code> field of the | |||||
* <code>CONSTANT_MethodHandle_info</code> structure | |||||
* at the given index. | |||||
* | |||||
* @since 3.17 | |||||
*/ | |||||
public int getMethodHandleIndex(int index) { | |||||
MethodHandleInfo mhinfo = (MethodHandleInfo)getItem(index); | |||||
return mhinfo.refIndex; | |||||
} | |||||
/** | |||||
* Reads the <code>descriptor_index</code> field of the | |||||
* <code>CONSTANT_MethodType_info</code> structure | |||||
* at the given index. | |||||
* | |||||
* @since 3.17 | |||||
*/ | |||||
public int getMethodTypeInfo(int index) { | |||||
MethodTypeInfo mtinfo = (MethodTypeInfo)getItem(index); | |||||
return mtinfo.descriptor; | |||||
} | |||||
/** | |||||
* Reads the <code>bootstrap_method_attr_index</code> field of the | |||||
* <code>CONSTANT_InvokeDynamic_info</code> structure | |||||
* at the given index. | |||||
* | |||||
* @since 3.17 | |||||
*/ | |||||
public int getInvokeDynamicBootstrap(int index) { | |||||
InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); | |||||
return iv.bootstrap; | |||||
} | |||||
/** | |||||
* Reads the <code>name_and_type_index</code> field of the | |||||
* <code>CONSTANT_InvokeDynamic_info</code> structure | |||||
* at the given index. | |||||
* | |||||
* @since 3.17 | |||||
*/ | |||||
public int getInvokeDynamicNameAndType(int index) { | |||||
InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); | |||||
return iv.nameAndType; | |||||
} | |||||
/** | |||||
* Reads the <code>descriptor_index</code> field of the | |||||
* <code>CONSTANT_NameAndType_info</code> structure | |||||
* indirectly specified by the given index. | |||||
* | |||||
* @param index an index to a <code>CONSTANT_InvokeDynamic_info</code>. | |||||
* @return the descriptor of the method. | |||||
* @since 3.17 | |||||
*/ | |||||
public String getInvokeDynamicType(int index) { | |||||
InvokeDynamicInfo iv = (InvokeDynamicInfo)getItem(index); | |||||
if (iv == null) | |||||
return null; | |||||
else { | |||||
NameAndTypeInfo n = (NameAndTypeInfo)getItem(iv.nameAndType); | |||||
if(n == null) | |||||
return null; | |||||
else | |||||
return getUtf8Info(n.typeDescriptor); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Determines whether <code>CONSTANT_Methodref_info</code> | * Determines whether <code>CONSTANT_Methodref_info</code> | ||||
* structure at the given index represents the constructor | * structure at the given index represents the constructor | ||||
return addItem(new Utf8Info(utf8, numOfItems)); | return addItem(new Utf8Info(utf8, numOfItems)); | ||||
} | } | ||||
/** | |||||
* Adds a new <code>CONSTANT_MethodHandle_info</code> | |||||
* structure. | |||||
* | |||||
* @param kind <code>reference_kind</code> | |||||
* such as {@link #REF_invokeStatic <code>REF_invokeStatic</code>}. | |||||
* @param index <code>reference_index</code>. | |||||
* @return the index of the added entry. | |||||
* | |||||
* @since 3.17 | |||||
*/ | |||||
public int addMethodHandleInfo(int kind, int index) { | |||||
return addItem(new MethodHandleInfo(kind, index, numOfItems)); | |||||
} | |||||
/** | |||||
* Adds a new <code>CONSTANT_MethodType_info</code> | |||||
* structure. | |||||
* | |||||
* @param desc <code>descriptor_index</code>. | |||||
* @return the index of the added entry. | |||||
* | |||||
* @since 3.17 | |||||
*/ | |||||
public int addMethodTypeInfo(int desc) { | |||||
return addItem(new MethodTypeInfo(desc, numOfItems)); | |||||
} | |||||
/** | |||||
* Adds a new <code>CONSTANT_InvokeDynamic_info</code> | |||||
* structure. | |||||
* | |||||
* @param bootstrap <code>bootstrap_method_attr_index</code>. | |||||
* @param nameAndType <code>name_and_type_index</code>. | |||||
* @return the index of the added entry. | |||||
* | |||||
* @since 3.17 | |||||
*/ | |||||
public int addInvokeDynamicInfo(int bootstrap, int nameAndType) { | |||||
return addItem(new InvokeDynamicInfo(bootstrap, nameAndType, numOfItems)); | |||||
} | |||||
/** | /** | ||||
* Get all the class names. | * Get all the class names. | ||||
* | * | ||||
case NameAndTypeInfo.tag : // 12 | case NameAndTypeInfo.tag : // 12 | ||||
info = new NameAndTypeInfo(in, numOfItems); | info = new NameAndTypeInfo(in, numOfItems); | ||||
break; | break; | ||||
case MethodHandleInfo.tag : // 15 | |||||
info = new MethodHandleInfo(in, numOfItems); | |||||
break; | |||||
case MethodTypeInfo.tag : // 16 | |||||
info = new MethodTypeInfo(in, numOfItems); | |||||
break; | |||||
case InvokeDynamicInfo.tag : // 18 | |||||
info = new InvokeDynamicInfo(in, numOfItems); | |||||
break; | |||||
default : | default : | ||||
throw new IOException("invalid constant type: " + tag + " at " + numOfItems); | throw new IOException("invalid constant type: " + tag + " at " + numOfItems); | ||||
} | } | ||||
out.println("\""); | out.println("\""); | ||||
} | } | ||||
} | } | ||||
class MethodHandleInfo extends ConstInfo { | |||||
static final int tag = 15; | |||||
int refKind, refIndex; | |||||
public MethodHandleInfo(int kind, int referenceIndex, int index) { | |||||
super(index); | |||||
refKind = kind; | |||||
refIndex = referenceIndex; | |||||
} | |||||
public MethodHandleInfo(DataInputStream in, int index) throws IOException { | |||||
super(index); | |||||
refKind = in.readUnsignedByte(); | |||||
refIndex = in.readUnsignedShort(); | |||||
} | |||||
public int hashCode() { return (refKind << 16) ^ refIndex; } | |||||
public boolean equals(Object obj) { | |||||
if (obj instanceof MethodHandleInfo) { | |||||
MethodHandleInfo mh = (MethodHandleInfo)obj; | |||||
return mh.refKind == refKind && mh.refIndex == refIndex; | |||||
} | |||||
else | |||||
return false; | |||||
} | |||||
public int getTag() { return tag; } | |||||
public int copy(ConstPool src, ConstPool dest, Map map) { | |||||
return dest.addMethodHandleInfo(refKind, | |||||
src.getItem(refIndex).copy(src, dest, map)); | |||||
} | |||||
public void write(DataOutputStream out) throws IOException { | |||||
out.writeByte(tag); | |||||
out.writeByte(refKind); | |||||
out.writeShort(refIndex); | |||||
} | |||||
public void print(PrintWriter out) { | |||||
out.print("MethodHandle #"); | |||||
out.print(refKind); | |||||
out.print(", index #"); | |||||
out.println(refIndex); | |||||
} | |||||
} | |||||
class MethodTypeInfo extends ConstInfo { | |||||
static final int tag = 16; | |||||
int descriptor; | |||||
public MethodTypeInfo(int desc, int index) { | |||||
super(index); | |||||
descriptor = desc; | |||||
} | |||||
public MethodTypeInfo(DataInputStream in, int index) throws IOException { | |||||
super(index); | |||||
descriptor = in.readUnsignedShort(); | |||||
} | |||||
public int hashCode() { return descriptor; } | |||||
public boolean equals(Object obj) { | |||||
if (obj instanceof MethodTypeInfo) | |||||
return ((MethodTypeInfo)obj).descriptor == descriptor; | |||||
else | |||||
return false; | |||||
} | |||||
public int getTag() { return tag; } | |||||
public void renameClass(ConstPool cp, String oldName, String newName, HashMap cache) { | |||||
String desc = cp.getUtf8Info(descriptor); | |||||
String desc2 = Descriptor.rename(desc, oldName, newName); | |||||
if (desc != desc2) | |||||
if (cache == null) | |||||
descriptor = cp.addUtf8Info(desc2); | |||||
else { | |||||
cache.remove(this); | |||||
descriptor = cp.addUtf8Info(desc2); | |||||
cache.put(this, this); | |||||
} | |||||
} | |||||
public void renameClass(ConstPool cp, Map map, HashMap cache) { | |||||
String desc = cp.getUtf8Info(descriptor); | |||||
String desc2 = Descriptor.rename(desc, map); | |||||
if (desc != desc2) | |||||
if (cache == null) | |||||
descriptor = cp.addUtf8Info(desc2); | |||||
else { | |||||
cache.remove(this); | |||||
descriptor = cp.addUtf8Info(desc2); | |||||
cache.put(this, this); | |||||
} | |||||
} | |||||
public int copy(ConstPool src, ConstPool dest, Map map) { | |||||
String desc = src.getUtf8Info(descriptor); | |||||
desc = Descriptor.rename(desc, map); | |||||
return dest.addMethodTypeInfo(dest.addUtf8Info(desc)); | |||||
} | |||||
public void write(DataOutputStream out) throws IOException { | |||||
out.writeByte(tag); | |||||
out.writeShort(descriptor); | |||||
} | |||||
public void print(PrintWriter out) { | |||||
out.print("MethodType #"); | |||||
out.println(descriptor); | |||||
} | |||||
} | |||||
class InvokeDynamicInfo extends ConstInfo { | |||||
static final int tag = 18; | |||||
int bootstrap, nameAndType; | |||||
public InvokeDynamicInfo(int bootstrapMethod, int ntIndex, int index) { | |||||
super(index); | |||||
bootstrap = bootstrapMethod; | |||||
nameAndType = ntIndex; | |||||
} | |||||
public InvokeDynamicInfo(DataInputStream in, int index) throws IOException { | |||||
super(index); | |||||
bootstrap = in.readUnsignedShort(); | |||||
nameAndType = in.readUnsignedShort(); | |||||
} | |||||
public int hashCode() { return (bootstrap << 16) ^ nameAndType; } | |||||
public boolean equals(Object obj) { | |||||
if (obj instanceof InvokeDynamicInfo) { | |||||
InvokeDynamicInfo iv = (InvokeDynamicInfo)obj; | |||||
return iv.bootstrap == bootstrap && iv.nameAndType == nameAndType; | |||||
} | |||||
else | |||||
return false; | |||||
} | |||||
public int getTag() { return tag; } | |||||
public int copy(ConstPool src, ConstPool dest, Map map) { | |||||
return dest.addInvokeDynamicInfo(bootstrap, | |||||
src.getItem(nameAndType).copy(src, dest, map)); | |||||
} | |||||
public void write(DataOutputStream out) throws IOException { | |||||
out.writeByte(tag); | |||||
out.writeShort(bootstrap); | |||||
out.writeShort(nameAndType); | |||||
} | |||||
public void print(PrintWriter out) { | |||||
out.print("InvokeDynamic #"); | |||||
out.print(bootstrap); | |||||
out.print(", name&type #"); | |||||
out.println(nameAndType); | |||||
} | |||||
} |
return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1)); | return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1)); | ||||
case INVOKEINTERFACE: | case INVOKEINTERFACE: | ||||
return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1)); | return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1)); | ||||
case 186: | |||||
throw new RuntimeException("Bad opcode 186"); | |||||
case INVOKEDYNAMIC: | |||||
return opstring + " " + iter.u16bitAt(pos + 1); | |||||
case NEW: | case NEW: | ||||
return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); | return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); | ||||
case NEWARRAY: | case NEWARRAY: |
/** | /** | ||||
* <code>method_info</code> structure. | * <code>method_info</code> structure. | ||||
* | |||||
* <p>The bytecode sequence of the method is represented | |||||
* by a <code>CodeAttribute</code> object. | |||||
* | * | ||||
* @see #getCodeAttribute() | |||||
* @see CodeAttribute | |||||
* @see javassist.CtMethod#getMethodInfo() | * @see javassist.CtMethod#getMethodInfo() | ||||
* @see javassist.CtConstructor#getMethodInfo() | * @see javassist.CtConstructor#getMethodInfo() | ||||
*/ | */ | ||||
* @param cf rebuild if this class file is for Java 6 or later. | * @param cf rebuild if this class file is for Java 6 or later. | ||||
* @see #rebuildStackMap(ClassPool) | * @see #rebuildStackMap(ClassPool) | ||||
* @see #rebuildStackMapForME(ClassPool) | * @see #rebuildStackMapForME(ClassPool) | ||||
* @see #doPreverify | |||||
* @since 3.6 | * @since 3.6 | ||||
*/ | */ | ||||
public void rebuildStackMapIf6(ClassPool pool, ClassFile cf) | public void rebuildStackMapIf6(ClassPool pool, ClassFile cf) |
/** | /** | ||||
* The instruction names (mnemonics) sorted by the opcode. | * The instruction names (mnemonics) sorted by the opcode. | ||||
* The length of this array is 202 (jsr_w=201). | * The length of this array is 202 (jsr_w=201). | ||||
* | |||||
* <p>The value at index 186 is null since no instruction is | |||||
* assigned to 186. | |||||
*/ | */ | ||||
String[] OPCODE = { | String[] OPCODE = { | ||||
"nop", /* 0*/ | "nop", /* 0*/ | ||||
"invokespecial", /* 183*/ | "invokespecial", /* 183*/ | ||||
"invokestatic", /* 184*/ | "invokestatic", /* 184*/ | ||||
"invokeinterface", /* 185*/ | "invokeinterface", /* 185*/ | ||||
null, | |||||
"invokedynamic", /* 186 */ | |||||
"new", /* 187*/ | "new", /* 187*/ | ||||
"newarray", /* 188*/ | "newarray", /* 188*/ | ||||
"anewarray", /* 189*/ | "anewarray", /* 189*/ |
int IMUL = 104; | int IMUL = 104; | ||||
int INEG = 116; | int INEG = 116; | ||||
int INSTANCEOF = 193; | int INSTANCEOF = 193; | ||||
int INVOKEDYNAMIC = 186; | |||||
int INVOKEINTERFACE = 185; | int INVOKEINTERFACE = 185; | ||||
int INVOKESPECIAL = 183; | int INVOKESPECIAL = 183; | ||||
int INVOKESTATIC = 184; | int INVOKESTATIC = 184; | ||||
0, // invokespecial, 183 depends on the type | 0, // invokespecial, 183 depends on the type | ||||
0, // invokestatic, 184 depends on the type | 0, // invokestatic, 184 depends on the type | ||||
0, // invokeinterface, 185 depends on the type | 0, // invokeinterface, 185 depends on the type | ||||
0, // undefined, 186 | |||||
0, // invokedynaimc, 186 depends on the type | |||||
1, // new, 187 | 1, // new, 187 | ||||
0, // newarray, 188 | 0, // newarray, 188 | ||||
0, // anewarray, 189 | 0, // anewarray, 189 |
newDelta = offsetDelta - gap; | newDelta = offsetDelta - gap; | ||||
else if (where == oldPos) | else if (where == oldPos) | ||||
newDelta = offsetDelta + gap; | newDelta = offsetDelta + gap; | ||||
// else if (gap > 0 && oldPos < where && where < position) // chiba | |||||
// throw new RuntimeException("old:" + oldPos + " where:" + where + " pos:" + position); | |||||
else | else | ||||
return; | return; | ||||
newDelta = offsetDelta - gap; | newDelta = offsetDelta - gap; | ||||
else if (where == oldPos) | else if (where == oldPos) | ||||
newDelta = offsetDelta + gap; | newDelta = offsetDelta + gap; | ||||
// else if (gap > 0 && oldPos < where && where < position) // chiba | |||||
// throw new RuntimeException("old:" + oldPos + " where:" + where + " pos:" + position); | |||||
else | else | ||||
return; | return; | ||||
case INVOKEINTERFACE: | case INVOKEINTERFACE: | ||||
evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame); | evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame); | ||||
break; | break; | ||||
case 186: | |||||
throw new RuntimeException("Bad opcode 186"); | |||||
case INVOKEDYNAMIC: | |||||
evalInvokeDynamic(opcode, iter.u16bitAt(pos + 1), frame); | |||||
break; | |||||
case NEW: | case NEW: | ||||
frame.push(resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1)))); | frame.push(resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1)))); | ||||
break; | break; | ||||
simplePush(zeroExtend(returnType), frame); | simplePush(zeroExtend(returnType), frame); | ||||
} | } | ||||
private void evalInvokeDynamic(int opcode, int index, Frame frame) throws BadBytecode { | |||||
String desc = constPool.getInvokeDynamicType(index); | |||||
Type[] types = paramTypesFromDesc(desc); | |||||
int i = types.length; | |||||
while (i > 0) | |||||
verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); | |||||
// simplePop(frame); // assume CosntPool#REF_invokeStatic | |||||
Type returnType = returnTypeFromDesc(desc); | |||||
if (returnType != Type.VOID) | |||||
simplePush(zeroExtend(returnType), frame); | |||||
} | |||||
private void evalLDC(int index, Frame frame) throws BadBytecode { | private void evalLDC(int index, Frame frame) throws BadBytecode { | ||||
int tag = constPool.getTag(index); | int tag = constPool.getTag(index); |
return doInvokeMethod(pos, code, false); | return doInvokeMethod(pos, code, false); | ||||
case Opcode.INVOKEINTERFACE : | case Opcode.INVOKEINTERFACE : | ||||
return doInvokeIntfMethod(pos, code); | return doInvokeIntfMethod(pos, code); | ||||
case 186 : | |||||
throw new RuntimeException("bad opcode 186"); | |||||
case Opcode.INVOKEDYNAMIC : | |||||
return doInvokeDynamic(pos, code); | |||||
case Opcode.NEW : { | case Opcode.NEW : { | ||||
int i = ByteArray.readU16bit(code, pos + 1); | int i = ByteArray.readU16bit(code, pos + 1); | ||||
stackTypes[stackTop++] | stackTypes[stackTop++] | ||||
return 5; | return 5; | ||||
} | } | ||||
private int doInvokeDynamic(int pos, byte[] code) throws BadBytecode { | |||||
int i = ByteArray.readU16bit(code, pos + 1); | |||||
String desc = cpool.getInvokeDynamicType(i); | |||||
checkParamTypes(desc, 1); | |||||
// assume CosntPool#REF_invokeStatic | |||||
/* TypeData target = stackTypes[--stackTop]; | |||||
if (target instanceof TypeData.UninitTypeVar && target.isUninit()) | |||||
constructorCalled((TypeData.UninitTypeVar)target); | |||||
*/ | |||||
pushMemberType(desc); | |||||
return 5; | |||||
} | |||||
private void pushMemberType(String descriptor) { | private void pushMemberType(String descriptor) { | ||||
int top = 0; | int top = 0; | ||||
if (descriptor.charAt(0) == '(') { | if (descriptor.charAt(0) == '(') { |
} | } | ||||
public void testJIRA150b() throws Exception { | public void testJIRA150b() throws Exception { | ||||
int origSize = javassist.compiler.MemberResolver.getInvalidMapSize(); | |||||
int N = 100; | int N = 100; | ||||
for (int k = 0; k < N; k++) { | for (int k = 0; k < N; k++) { | ||||
ClassPool pool = new ClassPool(true); | ClassPool pool = new ClassPool(true); | ||||
" int n5 = java.lang.Integer#valueOf(5); " + | " int n5 = java.lang.Integer#valueOf(5); " + | ||||
" return n1+n2+n3+n4+n5; }"); | " return n1+n2+n3+n4+n5; }"); | ||||
} | } | ||||
pool = null; | |||||
} | |||||
// try to run garbage collection. | |||||
int[] large; | |||||
for (int i = 0; i < 100; i++) { | |||||
large = new int[1000000]; | |||||
large[large.length - 2] = 9; | |||||
} | } | ||||
System.gc(); | System.gc(); | ||||
System.gc(); | System.gc(); | ||||
int size = javassist.compiler.MemberResolver.getInvalidMapSize(); | int size = javassist.compiler.MemberResolver.getInvalidMapSize(); | ||||
System.out.println("JIRA150b " + size); | System.out.println("JIRA150b " + size); | ||||
assertTrue("JIRA150b size: " + size, size < N - 10); | |||||
assertTrue("JIRA150b size: " + origSize + " " + size, size < origSize + N); | |||||
} | } | ||||
public void testJIRA152() throws Exception { | public void testJIRA152() throws Exception { |
assertEquals("[Ltest.Bar2;", cp.getClassInfo(n8)); | assertEquals("[Ltest.Bar2;", cp.getClassInfo(n8)); | ||||
} | } | ||||
public void testInvokeDynamic() throws Exception { | |||||
CtClass cc = loader.get("test4.InvokeDyn"); | |||||
ClassFile cf = cc.getClassFile(); | |||||
ConstPool cp = cf.getConstPool(); | |||||
Bytecode code = new Bytecode(cp, 0, 1); | |||||
code.addAload(0); | |||||
code.addIconst(9); | |||||
code.addLdc("nine"); | |||||
code.addInvokedynamic(0, "call", "(ILjava/lang/String;)I"); | |||||
code.addOpcode(Opcode.SWAP); | |||||
code.addOpcode(Opcode.POP); | |||||
code.addOpcode(Opcode.IRETURN); | |||||
MethodInfo minfo = new MethodInfo(cp, "test", "()I"); | |||||
minfo.setCodeAttribute(code.toCodeAttribute()); | |||||
minfo.setAccessFlags(AccessFlag.PUBLIC); | |||||
minfo.rebuildStackMapIf6(loader, cf); | |||||
cf.addMethod(minfo); | |||||
cf.addMethod(new MethodInfo(cp, "test2", minfo, null)); | |||||
int mtIndex = cp.addMethodTypeInfo(cp.addUtf8Info("(I)V")); | |||||
String desc | |||||
= "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)" + | |||||
"Ljava/lang/invoke/CallSite;"; | |||||
int mri = cp.addMethodrefInfo(cp.addClassInfo(cc.getName()), "boot", desc); | |||||
int mhi = cp.addMethodHandleInfo(ConstPool.REF_invokeStatic, mri); | |||||
int[] args = new int[0]; | |||||
BootstrapMethodsAttribute.BootstrapMethod[] bms | |||||
= new BootstrapMethodsAttribute.BootstrapMethod[1]; | |||||
bms[0] = new BootstrapMethodsAttribute.BootstrapMethod(mhi, args); | |||||
cf.addAttribute(new BootstrapMethodsAttribute(cp, bms)); | |||||
cc.writeFile(); | |||||
Object obj = make(cc.getName()); | |||||
assertEquals(9, invoke(obj, "test")); | |||||
ClassPool cp2 = new ClassPool(); | |||||
cp2.appendClassPath("."); | |||||
CtClass cc2 = cp2.get(cc.getName()); | |||||
assertEquals("test4.InvokeDyn", cc2.getClassFile().getName()); | |||||
ConstPool cPool2 = cc2.getClassFile().getConstPool(); | |||||
assertEquals("(I)V", cPool2.getUtf8Info(cPool2.getMethodTypeInfo(mtIndex))); | |||||
} | |||||
public static void main(String[] args) { | public static void main(String[] args) { | ||||
// junit.textui.TestRunner.run(suite()); | // junit.textui.TestRunner.run(suite()); | ||||
junit.awtui.TestRunner.main(new String[] { | junit.awtui.TestRunner.main(new String[] { |
} | } | ||||
} | } | ||||
public void testSwitchCase3() throws Exception { | |||||
CtClass cc = loader.get("javassist.bytecode.StackMapTest$T7c"); | |||||
StringBuffer sbuf = new StringBuffer("String s;"); | |||||
for (int i = 0; i < 130; i++) | |||||
sbuf.append("s =\"" + i + "\";"); | |||||
cc.getDeclaredMethod("foo").insertBefore(sbuf.toString()); | |||||
cc.getDeclaredMethod("test2").setBody(loader.get("javassist.bytecode.StackMapTest$T8c").getDeclaredMethod("test2"), null); | |||||
cc.writeFile(); | |||||
Object t1 = make(cc.getName()); | |||||
assertEquals(100, invoke(t1, "test")); | |||||
} | |||||
public static class T7c { | |||||
int value = 1; | |||||
T7b t7; | |||||
public static T7b make2() { return null; } | |||||
public static void print(String s) {} | |||||
public int foo() { return 1; } | |||||
public int test() { return test2(10); } | |||||
public int test2(int k) { return 0; } | |||||
} | |||||
public static class T8c { | |||||
public int test2(int k) { | |||||
int jj = 50; | |||||
if (k > 0) | |||||
k += "fooo".length(); | |||||
int j = 50; | |||||
loop: for (int i = 0; i < 10; i++) { | |||||
int jjj = 1; | |||||
switch (i) { | |||||
case 0: | |||||
k++; | |||||
{ int poi = 0; } | |||||
break; | |||||
case 9: | |||||
break loop; | |||||
case 7: | |||||
k += jjj; | |||||
break; | |||||
} | |||||
} | |||||
return j + jj; | |||||
} | |||||
} | |||||
public void testSwitchCase4() throws Exception { | |||||
CtClass cc = loader.get("javassist.bytecode.StackMapTest$T8e"); | |||||
StringBuffer sbuf = new StringBuffer("String s;"); | |||||
for (int i = 0; i < 130; i++) | |||||
sbuf.append("s =\"" + i + "\";"); | |||||
cc.getDeclaredMethod("foo").insertBefore(sbuf.toString()); | |||||
CtClass orig = loader.get("javassist.bytecode.StackMapTest$T8d"); | |||||
CtMethod origM = orig.getDeclaredMethod("test2"); | |||||
writeLdcw(origM); | |||||
cc.getDeclaredMethod("test2").setBody(origM, null); | |||||
orig.writeFile(); | |||||
cc.writeFile(); | |||||
Object t1 = make(cc.getName()); | |||||
assertEquals(100, invoke(t1, "test")); | |||||
} | |||||
private void writeLdcw(CtMethod method) { | |||||
CodeAttribute ca = method.getMethodInfo().getCodeAttribute(); | |||||
int index = ca.getConstPool().addStringInfo("ldcw"); | |||||
CodeIterator ci = ca.iterator(); | |||||
ci.writeByte(Opcode.LDC_W, 14); | |||||
ci.write16bit(index, 15); | |||||
ci.writeByte(Opcode.LDC_W, 43); | |||||
ci.write16bit(index, 44); | |||||
} | |||||
public static class T8e { | |||||
static T8dd helper() { return new T8dd(); } | |||||
int integer() { return 9; } | |||||
boolean integer2(String s) { return true; } | |||||
static void print(String s) {} | |||||
private void print2(String s) {} | |||||
private Object func(String s) { return null; } | |||||
I8d func2(String s) { return null; } | |||||
static String ldcw() { return "k"; } | |||||
static boolean debug = false; | |||||
void log(String p1,String p2,String p3,Long p4, Object p5, Object p6) {} | |||||
public int foo() { return 1; } | |||||
public int test() { | |||||
try { | |||||
return test2("", 10) ? 1 : 0; | |||||
} catch (Exception e) {} | |||||
return 100; | |||||
} | |||||
public boolean test2(String s, int k) throws Exception { return false; } | |||||
} | |||||
public static interface I8d { | |||||
String foo(String s, int i); | |||||
} | |||||
public static class T8dd { | |||||
java.util.List foo(String s) { return new java.util.ArrayList(); } | |||||
} | |||||
public static class T8d { | |||||
static T8dd helper() { return new T8dd(); } | |||||
int integer() { return 9; } | |||||
boolean integer2(String s) { return true; } | |||||
static void print(String s) {} | |||||
private void print2(String s) {} | |||||
private Object func(String s) { return null; } | |||||
I8d func2(String s) { return null; } | |||||
static String ldcw() { return "k"; } | |||||
static boolean debug = false; | |||||
void log(String p1,String p2,String p3,Long p4, Object p5, Object p6) {} | |||||
public boolean test2(String s, int i) throws Exception { | |||||
String k = null; | |||||
Object k2 = null; | |||||
boolean v5 = true; | |||||
if (!debug) | |||||
print(ldcw()); | |||||
Object k3 = this.func(s); | |||||
if (k3 == null) | |||||
throw new Exception(new StringBuilder().append(ldcw()).append(s).append(",").toString()); | |||||
String v7 = k3.toString(); | |||||
k2 = this.func2(v7); | |||||
if (k2 != null) { // 82: | |||||
if (k2 instanceof I8d) { | |||||
I8d k5 = (I8d)k2; | |||||
k = k5.foo(s, i); | |||||
} | |||||
} | |||||
java.util.List list = helper().foo(v7); // local 8 | |||||
for (int v9 = 0; v9 < list.size(); v9++) { | |||||
boolean v10 = true; | |||||
T8d v11 = (T8d)list.get(v9); | |||||
switch (v11.integer()) { | |||||
case 1: | |||||
break; | |||||
case 2: | |||||
v10 = this.integer2(s); | |||||
v5 &= v10; | |||||
break; | |||||
default : | |||||
throw new Exception(new StringBuilder().append("ldc 189").append(v11.integer()).append("ldc 169").toString()); | |||||
} | |||||
} | |||||
if (v5) // 246: | |||||
this.print2(s); | |||||
if (v5) | |||||
this.log(ldcw(), v7, s, Long.valueOf(new Integer(i).longValue()), k, null); | |||||
else // 290: | |||||
this.log(ldcw(), v7, s, Long.valueOf(new Integer(i).longValue()), k, null); | |||||
return v5; | |||||
} | |||||
} | |||||
public void tstCtClassType() throws Exception { | public void tstCtClassType() throws Exception { | ||||
ClassPool cp = ClassPool.getDefault(); | ClassPool cp = ClassPool.getDefault(); | ||||
CtClass cc = cp.get("javassist.CtClassType"); | CtClass cc = cp.get("javassist.CtClassType"); |
package test4; | |||||
import java.lang.invoke.*; | |||||
public class InvokeDyn { | |||||
public static int test9(int i, String s) { return 9; } | |||||
public static CallSite boot(MethodHandles.Lookup caller, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { | |||||
MethodHandles.Lookup lookup = MethodHandles.lookup(); | |||||
Class thisClass = lookup.lookupClass(); | |||||
MethodHandle method = lookup.findStatic(thisClass, "test9", MethodType.methodType(int.class, int.class, String.class)); | |||||
return new ConstantCallSite(method); | |||||
} | |||||
} |