git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@669 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -26,6 +26,11 @@ import javassist.expr.ExprEditor; | |||
* or a static constructor (class initializer). | |||
* It is the abstract super class of | |||
* <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 { | |||
protected MethodInfo methodInfo; |
@@ -77,6 +77,8 @@ public class AttributeInfo { | |||
if (nameStr.charAt(0) < 'L') { | |||
if (nameStr.equals(AnnotationDefaultAttribute.tag)) | |||
return new AnnotationDefaultAttribute(cp, name, in); | |||
else if (nameStr.equals(BootstrapMethodsAttribute.tag)) | |||
return new BootstrapMethodsAttribute(cp, name, in); | |||
else if (nameStr.equals(CodeAttribute.tag)) | |||
return new CodeAttribute(cp, name, in); | |||
else if (nameStr.equals(ConstantAttribute.tag)) |
@@ -0,0 +1,123 @@ | |||
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); | |||
} | |||
} |
@@ -1083,7 +1083,7 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode { | |||
public void addInvokevirtual(int clazz, String name, String desc) { | |||
add(INVOKEVIRTUAL); | |||
addIndex(constPool.addMethodrefInfo(clazz, name, desc)); | |||
growStack(Descriptor.dataSize(desc) - 1); | |||
growStack(Descriptor.dataSize(desc)); // assume CosntPool#REF_invokeStatic | |||
} | |||
/** | |||
@@ -1154,6 +1154,25 @@ public class Bytecode extends ByteVector implements Cloneable, Opcode { | |||
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> | |||
* object. |
@@ -240,6 +240,10 @@ class CodeAnalyzer implements Opcode { | |||
ci.u16bitAt(index + 1)); | |||
stack += Descriptor.dataSize(desc) - 1; | |||
break; | |||
case INVOKEDYNAMIC : | |||
desc = constPool.getInvokeDynamicType(ci.u16bitAt(index + 1)); | |||
stack += Descriptor.dataSize(desc); // assume CosntPool#REF_invokeStatic | |||
break; | |||
case ATHROW : | |||
stack = 1; // the stack becomes empty (1 means no values). | |||
break; |
@@ -32,6 +32,7 @@ import java.util.Map; | |||
* use <code>CodeIterator</code>. | |||
* | |||
* @see CodeIterator | |||
* @see #iterator() | |||
*/ | |||
public class CodeAttribute extends AttributeInfo implements Opcode { | |||
/** | |||
@@ -400,6 +401,12 @@ public class CodeAttribute extends AttributeInfo implements Opcode { | |||
newcode[i + 3] = code[i + 3]; | |||
newcode[i + 4] = code[i + 4]; | |||
break; | |||
case INVOKEDYNAMIC : | |||
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, | |||
classnameMap); | |||
newcode[i + 3] = 0; | |||
newcode[i + 4] = 0; | |||
break; | |||
case MULTIANEWARRAY : | |||
copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, | |||
classnameMap); |
@@ -21,6 +21,16 @@ import java.util.ArrayList; | |||
/** | |||
* 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 | |||
* same <code>Code_attribute</code>, then inserting a gap by one | |||
* <code>CodeIterator</code> will break the other | |||
@@ -725,11 +735,10 @@ public class CodeIterator implements Opcode { | |||
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, | |||
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 | |||
}; | |||
// 0 .. UNUSED (186), LOOKUPSWITCH, TABLESWITCH, WIDE | |||
// 0 .. LOOKUPSWITCH, TABLESWITCH, WIDE | |||
/** | |||
* Calculates the index of the next opcode. | |||
@@ -1496,7 +1505,9 @@ public class CodeIterator implements Opcode { | |||
int padding = 3 - (pos & 3); | |||
int nops = gap - padding; | |||
int bytecodeSize = 5 + (3 - (orgPos & 3)) + tableSize(); | |||
adjustOffsets(bytecodeSize, nops); | |||
if (nops > 0) | |||
adjustOffsets(bytecodeSize, nops); | |||
newcode[dest++] = code[src]; | |||
while (padding-- > 0) | |||
newcode[dest++] = 0; |
@@ -93,11 +93,61 @@ public final class ConstPool { | |||
*/ | |||
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. | |||
*/ | |||
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. | |||
* | |||
@@ -588,6 +638,97 @@ public final class ConstPool { | |||
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> | |||
* structure at the given index represents the constructor | |||
@@ -926,6 +1067,48 @@ public final class ConstPool { | |||
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. | |||
* | |||
@@ -1040,6 +1223,15 @@ public final class ConstPool { | |||
case NameAndTypeInfo.tag : // 12 | |||
info = new NameAndTypeInfo(in, numOfItems); | |||
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 : | |||
throw new IOException("invalid constant type: " + tag + " at " + numOfItems); | |||
} | |||
@@ -1632,3 +1824,167 @@ class Utf8Info extends ConstInfo { | |||
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); | |||
} | |||
} |
@@ -137,8 +137,8 @@ public class InstructionPrinter implements Opcode { | |||
return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1)); | |||
case INVOKEINTERFACE: | |||
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: | |||
return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); | |||
case NEWARRAY: |
@@ -27,7 +27,12 @@ import javassist.bytecode.stackmap.MapMaker; | |||
/** | |||
* <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.CtConstructor#getMethodInfo() | |||
*/ | |||
@@ -390,6 +395,7 @@ public class MethodInfo { | |||
* @param cf rebuild if this class file is for Java 6 or later. | |||
* @see #rebuildStackMap(ClassPool) | |||
* @see #rebuildStackMapForME(ClassPool) | |||
* @see #doPreverify | |||
* @since 3.6 | |||
*/ | |||
public void rebuildStackMapIf6(ClassPool pool, ClassFile cf) |
@@ -31,9 +31,6 @@ public interface Mnemonic { | |||
/** | |||
* The instruction names (mnemonics) sorted by the opcode. | |||
* 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 = { | |||
"nop", /* 0*/ | |||
@@ -222,7 +219,7 @@ public interface Mnemonic { | |||
"invokespecial", /* 183*/ | |||
"invokestatic", /* 184*/ | |||
"invokeinterface", /* 185*/ | |||
null, | |||
"invokedynamic", /* 186 */ | |||
"new", /* 187*/ | |||
"newarray", /* 188*/ | |||
"anewarray", /* 189*/ |
@@ -156,6 +156,7 @@ public interface Opcode { | |||
int IMUL = 104; | |||
int INEG = 116; | |||
int INSTANCEOF = 193; | |||
int INVOKEDYNAMIC = 186; | |||
int INVOKEINTERFACE = 185; | |||
int INVOKESPECIAL = 183; | |||
int INVOKESTATIC = 184; | |||
@@ -428,7 +429,7 @@ public interface Opcode { | |||
0, // invokespecial, 183 depends on the type | |||
0, // invokestatic, 184 depends on the type | |||
0, // invokeinterface, 185 depends on the type | |||
0, // undefined, 186 | |||
0, // invokedynaimc, 186 depends on the type | |||
1, // new, 187 | |||
0, // newarray, 188 | |||
0, // anewarray, 189 |
@@ -909,6 +909,8 @@ public class StackMapTable extends AttributeInfo { | |||
newDelta = offsetDelta - gap; | |||
else if (where == oldPos) | |||
newDelta = offsetDelta + gap; | |||
// else if (gap > 0 && oldPos < where && where < position) // chiba | |||
// throw new RuntimeException("old:" + oldPos + " where:" + where + " pos:" + position); | |||
else | |||
return; | |||
@@ -949,6 +951,8 @@ public class StackMapTable extends AttributeInfo { | |||
newDelta = offsetDelta - gap; | |||
else if (where == oldPos) | |||
newDelta = offsetDelta + gap; | |||
// else if (gap > 0 && oldPos < where && where < position) // chiba | |||
// throw new RuntimeException("old:" + oldPos + " where:" + where + " pos:" + position); | |||
else | |||
return; | |||
@@ -573,8 +573,9 @@ public class Executor implements Opcode { | |||
case INVOKEINTERFACE: | |||
evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame); | |||
break; | |||
case 186: | |||
throw new RuntimeException("Bad opcode 186"); | |||
case INVOKEDYNAMIC: | |||
evalInvokeDynamic(opcode, iter.u16bitAt(pos + 1), frame); | |||
break; | |||
case NEW: | |||
frame.push(resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1)))); | |||
break; | |||
@@ -748,6 +749,20 @@ public class Executor implements Opcode { | |||
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 { | |||
int tag = constPool.getTag(index); |
@@ -597,8 +597,8 @@ public abstract class Tracer implements TypeTag { | |||
return doInvokeMethod(pos, code, false); | |||
case Opcode.INVOKEINTERFACE : | |||
return doInvokeIntfMethod(pos, code); | |||
case 186 : | |||
throw new RuntimeException("bad opcode 186"); | |||
case Opcode.INVOKEDYNAMIC : | |||
return doInvokeDynamic(pos, code); | |||
case Opcode.NEW : { | |||
int i = ByteArray.readU16bit(code, pos + 1); | |||
stackTypes[stackTop++] | |||
@@ -835,6 +835,21 @@ public abstract class Tracer implements TypeTag { | |||
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) { | |||
int top = 0; | |||
if (descriptor.charAt(0) == '(') { |
@@ -662,6 +662,7 @@ public class JvstTest4 extends JvstTestRoot { | |||
} | |||
public void testJIRA150b() throws Exception { | |||
int origSize = javassist.compiler.MemberResolver.getInvalidMapSize(); | |||
int N = 100; | |||
for (int k = 0; k < N; k++) { | |||
ClassPool pool = new ClassPool(true); | |||
@@ -681,12 +682,20 @@ public class JvstTest4 extends JvstTestRoot { | |||
" int n5 = java.lang.Integer#valueOf(5); " + | |||
" 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(); | |||
int size = javassist.compiler.MemberResolver.getInvalidMapSize(); | |||
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 { |
@@ -779,6 +779,52 @@ public class BytecodeTest extends TestCase { | |||
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) { | |||
// junit.textui.TestRunner.run(suite()); | |||
junit.awtui.TestRunner.main(new String[] { |
@@ -423,6 +423,171 @@ public class StackMapTest extends TestCase { | |||
} | |||
} | |||
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 { | |||
ClassPool cp = ClassPool.getDefault(); | |||
CtClass cc = cp.get("javassist.CtClassType"); |
@@ -0,0 +1,14 @@ | |||
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); | |||
} | |||
} |