8294961: Convert java.base/java.lang.reflect.ProxyGenerator to use the Classfile API to generate proxy classes
Reviewed-by: mchung
This commit is contained in:
parent
96bfe613c3
commit
bee50cd330
@ -618,6 +618,7 @@ public class Proxy implements java.io.Serializable {
|
|||||||
private final List<Class<?>> interfaces;
|
private final List<Class<?>> interfaces;
|
||||||
private final ProxyClassContext context;
|
private final ProxyClassContext context;
|
||||||
ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
|
ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
|
||||||
|
Objects.requireNonNull(interfaces);
|
||||||
if (!VM.isModuleSystemInited()) {
|
if (!VM.isModuleSystemInited()) {
|
||||||
throw new InternalError("Proxy is not supported until "
|
throw new InternalError("Proxy is not supported until "
|
||||||
+ "module system is fully initialized");
|
+ "module system is fully initialized");
|
||||||
|
@ -25,16 +25,18 @@
|
|||||||
|
|
||||||
package java.lang.reflect;
|
package java.lang.reflect;
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
import java.lang.classfile.*;
|
||||||
import jdk.internal.org.objectweb.asm.Label;
|
import java.lang.classfile.constantpool.*;
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import java.lang.classfile.attribute.ExceptionsAttribute;
|
||||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
|
||||||
import jdk.internal.org.objectweb.asm.Type;
|
|
||||||
import sun.invoke.util.Wrapper;
|
|
||||||
import sun.security.action.GetBooleanAction;
|
import sun.security.action.GetBooleanAction;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.invoke.MethodType;
|
import static java.lang.classfile.ClassFile.*;
|
||||||
|
import java.lang.classfile.attribute.StackMapFrameInfo;
|
||||||
|
import java.lang.classfile.attribute.StackMapTableAttribute;
|
||||||
|
import java.lang.constant.ClassDesc;
|
||||||
|
import static java.lang.constant.ConstantDescs.*;
|
||||||
|
import java.lang.constant.MethodTypeDesc;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -43,8 +45,8 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
import java.util.function.IntFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProxyGenerator contains the code to generate a dynamic proxy class
|
* ProxyGenerator contains the code to generate a dynamic proxy class
|
||||||
@ -53,33 +55,34 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
* The external interface to ProxyGenerator is the static
|
* The external interface to ProxyGenerator is the static
|
||||||
* "generateProxyClass" method.
|
* "generateProxyClass" method.
|
||||||
*/
|
*/
|
||||||
final class ProxyGenerator extends ClassWriter {
|
final class ProxyGenerator {
|
||||||
private static final int CLASSFILE_VERSION = ClassFileFormatVersion.latest().major();
|
|
||||||
private static final String JL_CLASS = "java/lang/Class";
|
|
||||||
private static final String JL_OBJECT = "java/lang/Object";
|
|
||||||
private static final String JL_THROWABLE = "java/lang/Throwable";
|
|
||||||
private static final String JL_CLASS_NOT_FOUND_EX = "java/lang/ClassNotFoundException";
|
|
||||||
private static final String JL_ILLEGAL_ACCESS_EX = "java/lang/IllegalAccessException";
|
|
||||||
|
|
||||||
private static final String JL_NO_CLASS_DEF_FOUND_ERROR = "java/lang/NoClassDefFoundError";
|
private static final ClassDesc
|
||||||
private static final String JL_NO_SUCH_METHOD_EX = "java/lang/NoSuchMethodException";
|
CD_ClassLoader = ClassDesc.ofInternalName("java/lang/ClassLoader"),
|
||||||
private static final String JL_NO_SUCH_METHOD_ERROR = "java/lang/NoSuchMethodError";
|
CD_ClassNotFoundException = ClassDesc.ofInternalName("java/lang/ClassNotFoundException"),
|
||||||
private static final String JLI_LOOKUP = "java/lang/invoke/MethodHandles$Lookup";
|
CD_IllegalAccessException = ClassDesc.ofInternalName("java/lang/IllegalAccessException"),
|
||||||
private static final String JLI_METHODHANDLES = "java/lang/invoke/MethodHandles";
|
CD_InvocationHandler = ClassDesc.ofInternalName("java/lang/reflect/InvocationHandler"),
|
||||||
|
CD_Method = ClassDesc.ofInternalName("java/lang/reflect/Method"),
|
||||||
|
CD_NoClassDefFoundError = ClassDesc.ofInternalName("java/lang/NoClassDefFoundError"),
|
||||||
|
CD_NoSuchMethodError = ClassDesc.ofInternalName("java/lang/NoSuchMethodError"),
|
||||||
|
CD_NoSuchMethodException = ClassDesc.ofInternalName("java/lang/NoSuchMethodException"),
|
||||||
|
CD_Proxy = ClassDesc.ofInternalName("java/lang/reflect/Proxy"),
|
||||||
|
CD_UndeclaredThrowableException = ClassDesc.ofInternalName("java/lang/reflect/UndeclaredThrowableException");
|
||||||
|
|
||||||
private static final String JLR_INVOCATION_HANDLER = "java/lang/reflect/InvocationHandler";
|
private static final MethodTypeDesc
|
||||||
private static final String JLR_PROXY = "java/lang/reflect/Proxy";
|
MTD_boolean = MethodTypeDesc.of(CD_boolean),
|
||||||
private static final String JLR_UNDECLARED_THROWABLE_EX = "java/lang/reflect/UndeclaredThrowableException";
|
MTD_void_InvocationHandler = MethodTypeDesc.of(CD_void, CD_InvocationHandler),
|
||||||
|
MTD_void_String = MethodTypeDesc.of(CD_void, CD_String),
|
||||||
|
MTD_void_Throwable = MethodTypeDesc.of(CD_void, CD_Throwable),
|
||||||
|
MTD_Class = MethodTypeDesc.of(CD_Class),
|
||||||
|
MTD_Class_String_boolean_ClassLoader = MethodTypeDesc.of(CD_Class, CD_String, CD_boolean, CD_ClassLoader),
|
||||||
|
MTD_ClassLoader = MethodTypeDesc.of(CD_ClassLoader),
|
||||||
|
MTD_MethodHandles$Lookup = MethodTypeDesc.of(CD_MethodHandles_Lookup),
|
||||||
|
MTD_MethodHandles$Lookup_MethodHandles$Lookup = MethodTypeDesc.of(CD_MethodHandles_Lookup, CD_MethodHandles_Lookup),
|
||||||
|
MTD_Method_String_ClassArray = MethodTypeDesc.of(CD_Method, CD_String, CD_Class.arrayType()),
|
||||||
|
MTD_Object_Object_Method_ObjectArray = MethodTypeDesc.of(CD_Object, CD_Object, CD_Method, CD_Object.arrayType()),
|
||||||
|
MTD_String = MethodTypeDesc.of(CD_String);
|
||||||
|
|
||||||
private static final String LJL_CLASS = "Ljava/lang/Class;";
|
|
||||||
private static final String LJL_CLASSLOADER = "Ljava/lang/ClassLoader;";
|
|
||||||
private static final String LJLR_METHOD = "Ljava/lang/reflect/Method;";
|
|
||||||
private static final String LJLR_INVOCATION_HANDLER = "Ljava/lang/reflect/InvocationHandler;";
|
|
||||||
|
|
||||||
private static final String MJLR_INVOCATIONHANDLER = "(Ljava/lang/reflect/InvocationHandler;)V";
|
|
||||||
|
|
||||||
private static final String NAME_CTOR = "<init>";
|
|
||||||
private static final String NAME_CLINIT = "<clinit>";
|
|
||||||
private static final String NAME_LOOKUP_ACCESSOR = "proxyClassLookup";
|
private static final String NAME_LOOKUP_ACCESSOR = "proxyClassLookup";
|
||||||
|
|
||||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||||
@ -103,25 +106,122 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
private static final ProxyMethod equalsMethod;
|
private static final ProxyMethod equalsMethod;
|
||||||
private static final ProxyMethod toStringMethod;
|
private static final ProxyMethod toStringMethod;
|
||||||
|
|
||||||
|
private static final ClassModel TEMPLATE;
|
||||||
|
|
||||||
|
private static final ClassEntry CE_Class;
|
||||||
|
private static final ClassEntry CE_ClassNotFoundException;
|
||||||
|
private static final ClassEntry CE_NoClassDefFoundError;
|
||||||
|
private static final ClassEntry CE_NoSuchMethodError;
|
||||||
|
private static final ClassEntry CE_NoSuchMethodException;
|
||||||
|
private static final ClassEntry CE_Object;
|
||||||
|
private static final ClassEntry CE_Throwable;
|
||||||
|
private static final ClassEntry CE_UndeclaredThrowableException;
|
||||||
|
|
||||||
|
private static final FieldRefEntry FRE_Proxy_h;
|
||||||
|
|
||||||
|
private static final InterfaceMethodRefEntry IMRE_InvocationHandler_invoke;
|
||||||
|
|
||||||
|
private static final MethodRefEntry MRE_Class_forName;
|
||||||
|
private static final MethodRefEntry MRE_Class_getClassLoader;
|
||||||
|
private static final MethodRefEntry MRE_Class_getMethod;
|
||||||
|
private static final MethodRefEntry MRE_NoClassDefFoundError_init;
|
||||||
|
private static final MethodRefEntry MRE_NoSuchMethodError_init;
|
||||||
|
private static final MethodRefEntry MRE_Throwable_getMessage;
|
||||||
|
private static final MethodRefEntry MRE_UndeclaredThrowableException_init;
|
||||||
|
|
||||||
|
private static final Utf8Entry UE_Method;
|
||||||
|
|
||||||
|
private static final List<StackMapFrameInfo.VerificationTypeInfo> THROWABLE_STACK;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T extends PoolEntry> T entryByIndex(int index) {
|
||||||
|
return (T) TEMPLATE.constantPool().entryByIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
// static template ClassModel holds pre-defined constant pool entries
|
||||||
|
// proxy transformed from the template shares the template constant pool
|
||||||
|
// each direct use of the template pool entry is significantly faster
|
||||||
|
var cc = ClassFile.of();
|
||||||
|
var ei = new int[21];
|
||||||
|
TEMPLATE = cc.parse(cc.build(CD_Proxy, clb -> {
|
||||||
|
clb.withSuperclass(CD_Proxy);
|
||||||
|
generateConstructor(clb);
|
||||||
|
generateLookupAccessor(clb);
|
||||||
|
var cp = clb.constantPool();
|
||||||
|
|
||||||
|
ei[0] = cp.classEntry(CD_Class).index();
|
||||||
|
ei[1] = cp.classEntry(CD_ClassNotFoundException).index();
|
||||||
|
ei[2] = cp.classEntry(CD_NoClassDefFoundError).index();
|
||||||
|
ei[3] = cp.classEntry(CD_NoSuchMethodError).index();
|
||||||
|
ei[4] = cp.classEntry(CD_NoSuchMethodException).index();
|
||||||
|
ei[5] = cp.classEntry(CD_Object).index();
|
||||||
|
ei[6] = cp.classEntry(CD_Throwable).index();
|
||||||
|
ei[7] = cp.classEntry(CD_UndeclaredThrowableException).index();
|
||||||
|
|
||||||
|
ei[8] = cp.fieldRefEntry(CD_Proxy, handlerFieldName, CD_InvocationHandler).index();
|
||||||
|
|
||||||
|
ei[9] = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray).index();
|
||||||
|
|
||||||
|
ei[10] = cp.methodRefEntry(CD_Class, "forName", MTD_Class_String_boolean_ClassLoader).index();
|
||||||
|
ei[11] = cp.methodRefEntry(CD_Class, "getClassLoader", MTD_ClassLoader).index();
|
||||||
|
ei[12] = cp.methodRefEntry(CD_Class, "getMethod", MTD_Method_String_ClassArray).index();
|
||||||
|
ei[13] = cp.methodRefEntry(CD_NoClassDefFoundError, INIT_NAME, MTD_void_String).index();
|
||||||
|
ei[14] = cp.methodRefEntry(CD_NoSuchMethodError, INIT_NAME, MTD_void_String).index();
|
||||||
|
ei[15] = cp.methodRefEntry(CD_Throwable, "getMessage", MTD_String).index();
|
||||||
|
ei[16] = cp.methodRefEntry(CD_UndeclaredThrowableException, INIT_NAME, MTD_void_Throwable).index();
|
||||||
|
|
||||||
|
ei[17] = cp.utf8Entry(CD_Method).index();
|
||||||
|
|
||||||
|
ei[18] = cp.utf8Entry("m0").index();
|
||||||
|
ei[19] = cp.utf8Entry("m1").index();
|
||||||
|
ei[20] = cp.utf8Entry("m2").index();
|
||||||
|
}));
|
||||||
|
|
||||||
|
CE_Class = entryByIndex(ei[0]);
|
||||||
|
CE_ClassNotFoundException = entryByIndex(ei[1]);
|
||||||
|
CE_NoClassDefFoundError = entryByIndex(ei[2]);
|
||||||
|
CE_NoSuchMethodError = entryByIndex(ei[3]);
|
||||||
|
CE_NoSuchMethodException = entryByIndex(ei[4]);
|
||||||
|
CE_Object = entryByIndex(ei[5]);
|
||||||
|
CE_Throwable = entryByIndex(ei[6]);
|
||||||
|
CE_UndeclaredThrowableException = entryByIndex(ei[7]);
|
||||||
|
|
||||||
|
FRE_Proxy_h = entryByIndex(ei[8]);
|
||||||
|
|
||||||
|
IMRE_InvocationHandler_invoke = entryByIndex(ei[9]);
|
||||||
|
|
||||||
|
MRE_Class_forName = entryByIndex(ei[10]);
|
||||||
|
MRE_Class_getClassLoader = entryByIndex(ei[11]);
|
||||||
|
MRE_Class_getMethod = entryByIndex(ei[12]);
|
||||||
|
MRE_NoClassDefFoundError_init = entryByIndex(ei[13]);
|
||||||
|
MRE_NoSuchMethodError_init = entryByIndex(ei[14]);
|
||||||
|
MRE_Throwable_getMessage = entryByIndex(ei[15]);
|
||||||
|
MRE_UndeclaredThrowableException_init = entryByIndex(ei[16]);
|
||||||
|
|
||||||
|
UE_Method = entryByIndex(ei[17]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), "m0");
|
hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), entryByIndex(ei[18]));
|
||||||
equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), "m1");
|
equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), entryByIndex(ei[19]));
|
||||||
toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), "m2");
|
toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), entryByIndex(ei[20]));
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
throw new NoSuchMethodError(e.getMessage());
|
throw new NoSuchMethodError(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
THROWABLE_STACK = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(CE_Throwable));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class loader
|
* Classfile context
|
||||||
*/
|
*/
|
||||||
private final ClassLoader loader;
|
private final ClassFile classfileContext;
|
||||||
|
private final ConstantPoolBuilder cp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of proxy class
|
* Name of proxy class
|
||||||
*/
|
*/
|
||||||
private final String className;
|
private ClassEntry classEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proxy interfaces
|
* Proxy interfaces
|
||||||
@ -155,9 +255,12 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
*/
|
*/
|
||||||
private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces,
|
private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces,
|
||||||
int accessFlags) {
|
int accessFlags) {
|
||||||
super(ClassWriter.COMPUTE_FRAMES);
|
this.classfileContext = ClassFile.of(
|
||||||
this.loader = loader;
|
ClassFile.StackMapsOption.DROP_STACK_MAPS,
|
||||||
this.className = className;
|
ClassFile.ClassHierarchyResolverOption.of(
|
||||||
|
ClassHierarchyResolver.ofClassLoading(loader).cached()));
|
||||||
|
this.cp = ConstantPoolBuilder.of(TEMPLATE);
|
||||||
|
this.classEntry = cp.classEntry(ClassDesc.of(className));
|
||||||
this.interfaces = interfaces;
|
this.interfaces = interfaces;
|
||||||
this.accessFlags = accessFlags;
|
this.accessFlags = accessFlags;
|
||||||
}
|
}
|
||||||
@ -174,6 +277,7 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
final String name,
|
final String name,
|
||||||
List<Class<?>> interfaces,
|
List<Class<?>> interfaces,
|
||||||
int accessFlags) {
|
int accessFlags) {
|
||||||
|
Objects.requireNonNull(interfaces);
|
||||||
ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
|
ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
|
||||||
final byte[] classFile = gen.generateClassFile();
|
final byte[] classFile = gen.generateClassFile();
|
||||||
|
|
||||||
@ -185,7 +289,7 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
int i = name.lastIndexOf('.');
|
int i = name.lastIndexOf('.');
|
||||||
Path path;
|
Path path;
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
Path dir = Path.of(dotToSlash(name.substring(0, i)));
|
Path dir = Path.of(name.substring(0, i).replace('.', '/'));
|
||||||
Files.createDirectories(dir);
|
Files.createDirectories(dir);
|
||||||
path = dir.resolve(name.substring(i + 1) + ".class");
|
path = dir.resolve(name.substring(i + 1) + ".class");
|
||||||
} else {
|
} else {
|
||||||
@ -205,20 +309,22 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an array of the class and interface names from an array of Classes.
|
* {@return the entries of the given type}
|
||||||
*
|
* @param types the {@code Class} objects, not primitive types nor array types
|
||||||
* @param classes an array of classes or interfaces
|
|
||||||
* @return the array of class and interface names; or null if classes is
|
|
||||||
* null or empty
|
|
||||||
*/
|
*/
|
||||||
private static String[] typeNames(List<Class<?>> classes) {
|
private static ClassEntry[] toClassEntries(ConstantPoolBuilder cp, List<Class<?>> types) {
|
||||||
if (classes == null || classes.size() == 0)
|
var ces = new ClassEntry[types.size()];
|
||||||
return null;
|
for (int i = 0; i < ces.length; i++)
|
||||||
int size = classes.size();
|
ces[i] = cp.classEntry(cp.utf8Entry(types.get(i).getName().replace('.', '/')));
|
||||||
String[] ifaces = new String[size];
|
return ces;
|
||||||
for (int i = 0; i < size; i++)
|
}
|
||||||
ifaces[i] = dotToSlash(classes.get(i).getName());
|
|
||||||
return ifaces;
|
/**
|
||||||
|
* {@return the {@code ClassDesc} of the given type}
|
||||||
|
* @param type the {@code Class} object
|
||||||
|
*/
|
||||||
|
private static ClassDesc toClassDesc(Class<?> type) {
|
||||||
|
return ClassDesc.ofDescriptor(type.descriptorString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -387,34 +493,6 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
return uniqueList;
|
return uniqueList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a fully qualified class name that uses '.' as the package
|
|
||||||
* separator, the external representation used by the Java language
|
|
||||||
* and APIs, to a fully qualified class name that uses '/' as the
|
|
||||||
* package separator, the representation used in the class file
|
|
||||||
* format (see JVMS section {@jvms 4.2}).
|
|
||||||
*/
|
|
||||||
private static String dotToSlash(String name) {
|
|
||||||
return name.replace('.', '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the number of abstract "words", or consecutive local variable
|
|
||||||
* indexes, required to contain a value of the given type. See JVMS
|
|
||||||
* section {@jvms 3.6.1}.
|
|
||||||
* <p>
|
|
||||||
* Note that the original version of the JVMS contained a definition of
|
|
||||||
* this abstract notion of a "word" in section 3.4, but that definition
|
|
||||||
* was removed for the second edition.
|
|
||||||
*/
|
|
||||||
private static int getWordsPerType(Class<?> type) {
|
|
||||||
if (type == long.class || type == double.class) {
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add to the given list all of the types in the "from" array that
|
* Add to the given list all of the types in the "from" array that
|
||||||
* are not already contained in the list and are assignable to at
|
* are not already contained in the list and are assignable to at
|
||||||
@ -439,25 +517,11 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link ClassLoader} to be used by the default implementation of {@link
|
|
||||||
* #getCommonSuperClass(String, String)}, that of this {@link ClassWriter}'s runtime type by
|
|
||||||
* default.
|
|
||||||
*
|
|
||||||
* @return ClassLoader
|
|
||||||
*/
|
|
||||||
protected ClassLoader getClassLoader() {
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a class file for the proxy class. This method drives the
|
* Generate a class file for the proxy class. This method drives the
|
||||||
* class file generation process.
|
* class file generation process.
|
||||||
*/
|
*/
|
||||||
private byte[] generateClassFile() {
|
private byte[] generateClassFile() {
|
||||||
visit(CLASSFILE_VERSION, accessFlags, dotToSlash(className), null,
|
|
||||||
JLR_PROXY, typeNames(interfaces));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add proxy methods for the hashCode, equals,
|
* Add proxy methods for the hashCode, equals,
|
||||||
* and toString methods of java.lang.Object. This is done before
|
* and toString methods of java.lang.Object. This is done before
|
||||||
@ -475,7 +539,7 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
for (Class<?> intf : interfaces) {
|
for (Class<?> intf : interfaces) {
|
||||||
for (Method m : intf.getMethods()) {
|
for (Method m : intf.getMethods()) {
|
||||||
if (!Modifier.isStatic(m.getModifiers())) {
|
if (!Modifier.isStatic(m.getModifiers())) {
|
||||||
addProxyMethod(m, intf);
|
addProxyMethod(m, intf, cp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -488,22 +552,23 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
checkReturnTypes(sigmethods);
|
checkReturnTypes(sigmethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateConstructor();
|
return classfileContext.build(classEntry, cp, clb -> {
|
||||||
|
TEMPLATE.forEach(clb);
|
||||||
|
clb.withFlags(accessFlags);
|
||||||
|
clb.withInterfaces(toClassEntries(cp, interfaces));
|
||||||
|
|
||||||
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
|
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
|
||||||
for (ProxyMethod pm : sigmethods) {
|
for (ProxyMethod pm : sigmethods) {
|
||||||
// add static field for the Method object
|
// add static field for the Method object
|
||||||
visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
|
clb.withField(pm.methodFieldName, UE_Method, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
|
||||||
LJLR_METHOD, null, null);
|
|
||||||
|
|
||||||
// Generate code for proxy method
|
// Generate code for proxy method
|
||||||
pm.generateMethod(this, className);
|
pm.generateMethod(clb, classEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateStaticInitializer();
|
generateStaticInitializer(clb);
|
||||||
generateLookupAccessor();
|
});
|
||||||
return toByteArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -519,7 +584,7 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
* passed to the invocation handler's "invoke" method for a given
|
* passed to the invocation handler's "invoke" method for a given
|
||||||
* set of duplicate methods.
|
* set of duplicate methods.
|
||||||
*/
|
*/
|
||||||
private void addProxyMethod(Method m, Class<?> fromClass) {
|
private void addProxyMethod(Method m, Class<?> fromClass, ConstantPoolBuilder cp) {
|
||||||
Class<?> returnType = m.getReturnType();
|
Class<?> returnType = m.getReturnType();
|
||||||
Class<?>[] exceptionTypes = m.getSharedExceptionTypes();
|
Class<?>[] exceptionTypes = m.getSharedExceptionTypes();
|
||||||
|
|
||||||
@ -545,7 +610,7 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
}
|
}
|
||||||
sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType,
|
sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType,
|
||||||
exceptionTypes, fromClass,
|
exceptionTypes, fromClass,
|
||||||
"m" + proxyMethodCount++));
|
cp.utf8Entry("m" + proxyMethodCount++)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -563,82 +628,51 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
/**
|
/**
|
||||||
* Generate the constructor method for the proxy class.
|
* Generate the constructor method for the proxy class.
|
||||||
*/
|
*/
|
||||||
private void generateConstructor() {
|
private static void generateConstructor(ClassBuilder clb) {
|
||||||
MethodVisitor ctor = visitMethod(Modifier.PUBLIC, NAME_CTOR,
|
clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob
|
||||||
MJLR_INVOCATIONHANDLER, null, null);
|
.aload(cob.receiverSlot())
|
||||||
ctor.visitParameter(null, 0);
|
.aload(cob.parameterSlot(0))
|
||||||
ctor.visitCode();
|
.invokespecial(CD_Proxy, INIT_NAME, MTD_void_InvocationHandler)
|
||||||
ctor.visitVarInsn(ALOAD, 0);
|
.return_());
|
||||||
ctor.visitVarInsn(ALOAD, 1);
|
|
||||||
ctor.visitMethodInsn(INVOKESPECIAL, JLR_PROXY, NAME_CTOR,
|
|
||||||
MJLR_INVOCATIONHANDLER, false);
|
|
||||||
ctor.visitInsn(RETURN);
|
|
||||||
|
|
||||||
// Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
|
|
||||||
ctor.visitMaxs(-1, -1);
|
|
||||||
ctor.visitEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the static initializer method for the proxy class.
|
* Generate the static initializer method for the proxy class.
|
||||||
*/
|
*/
|
||||||
private void generateStaticInitializer() {
|
private void generateStaticInitializer(ClassBuilder clb) {
|
||||||
|
clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
|
||||||
MethodVisitor mv = visitMethod(Modifier.STATIC, NAME_CLINIT,
|
|
||||||
"()V", null, null);
|
|
||||||
mv.visitCode();
|
|
||||||
Label L_startBlock = new Label();
|
|
||||||
Label L_endBlock = new Label();
|
|
||||||
Label L_NoMethodHandler = new Label();
|
|
||||||
Label L_NoClassHandler = new Label();
|
|
||||||
|
|
||||||
mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_NoMethodHandler,
|
|
||||||
JL_NO_SUCH_METHOD_EX);
|
|
||||||
mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_NoClassHandler,
|
|
||||||
JL_CLASS_NOT_FOUND_EX);
|
|
||||||
|
|
||||||
// Put ClassLoader at local variable index 0, used by
|
// Put ClassLoader at local variable index 0, used by
|
||||||
// Class.forName(String, boolean, ClassLoader) calls
|
// Class.forName(String, boolean, ClassLoader) calls
|
||||||
mv.visitLdcInsn(Type.getObjectType(dotToSlash(className)));
|
cob.ldc(classEntry)
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, JL_CLASS,
|
.invokevirtual(MRE_Class_getClassLoader)
|
||||||
"getClassLoader", "()" + LJL_CLASSLOADER, false);
|
.astore(0);
|
||||||
mv.visitVarInsn(ASTORE, 0);
|
var ts = cob.newBoundLabel();
|
||||||
|
|
||||||
mv.visitLabel(L_startBlock);
|
|
||||||
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
|
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
|
||||||
for (ProxyMethod pm : sigmethods) {
|
for (ProxyMethod pm : sigmethods) {
|
||||||
pm.codeFieldInitialization(mv, className);
|
pm.codeFieldInitialization(cob, classEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mv.visitInsn(RETURN);
|
cob.return_();
|
||||||
mv.visitLabel(L_endBlock);
|
var c1 = cob.newBoundLabel();
|
||||||
// Generate exception handler
|
cob.exceptionCatch(ts, c1, c1, CE_NoSuchMethodException)
|
||||||
|
.new_(CE_NoSuchMethodError)
|
||||||
mv.visitLabel(L_NoMethodHandler);
|
.dup_x1()
|
||||||
mv.visitVarInsn(ASTORE, 1);
|
.swap()
|
||||||
mv.visitTypeInsn(Opcodes.NEW, JL_NO_SUCH_METHOD_ERROR);
|
.invokevirtual(MRE_Throwable_getMessage)
|
||||||
mv.visitInsn(DUP);
|
.invokespecial(MRE_NoSuchMethodError_init)
|
||||||
mv.visitVarInsn(ALOAD, 1);
|
.athrow();
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE,
|
var c2 = cob.newBoundLabel();
|
||||||
"getMessage", "()Ljava/lang/String;", false);
|
cob.exceptionCatch(ts, c1, c2, CE_ClassNotFoundException)
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, JL_NO_SUCH_METHOD_ERROR,
|
.new_(CE_NoClassDefFoundError)
|
||||||
"<init>", "(Ljava/lang/String;)V", false);
|
.dup_x1()
|
||||||
mv.visitInsn(ATHROW);
|
.swap()
|
||||||
|
.invokevirtual(MRE_Throwable_getMessage)
|
||||||
mv.visitLabel(L_NoClassHandler);
|
.invokespecial(MRE_NoClassDefFoundError_init)
|
||||||
mv.visitVarInsn(ASTORE, 1);
|
.athrow()
|
||||||
mv.visitTypeInsn(Opcodes.NEW, JL_NO_CLASS_DEF_FOUND_ERROR);
|
.with(StackMapTableAttribute.of(List.of(
|
||||||
mv.visitInsn(DUP);
|
StackMapFrameInfo.of(c1, List.of(), THROWABLE_STACK),
|
||||||
mv.visitVarInsn(ALOAD, 1);
|
StackMapFrameInfo.of(c2, List.of(), THROWABLE_STACK))));
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE,
|
});
|
||||||
"getMessage", "()Ljava/lang/String;", false);
|
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, JL_NO_CLASS_DEF_FOUND_ERROR,
|
|
||||||
"<init>", "(Ljava/lang/String;)V", false);
|
|
||||||
mv.visitInsn(ATHROW);
|
|
||||||
|
|
||||||
// Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
|
|
||||||
mv.visitMaxs(-1, -1);
|
|
||||||
mv.visitEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -646,39 +680,28 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
* on this proxy class if the caller's lookup class is java.lang.reflect.Proxy;
|
* on this proxy class if the caller's lookup class is java.lang.reflect.Proxy;
|
||||||
* otherwise, IllegalAccessException is thrown
|
* otherwise, IllegalAccessException is thrown
|
||||||
*/
|
*/
|
||||||
private void generateLookupAccessor() {
|
private static void generateLookupAccessor(ClassBuilder clb) {
|
||||||
MethodVisitor mv = visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_LOOKUP_ACCESSOR,
|
clb.withMethod(NAME_LOOKUP_ACCESSOR,
|
||||||
"(Ljava/lang/invoke/MethodHandles$Lookup;)Ljava/lang/invoke/MethodHandles$Lookup;", null,
|
MTD_MethodHandles$Lookup_MethodHandles$Lookup,
|
||||||
new String[] { JL_ILLEGAL_ACCESS_EX });
|
ACC_PRIVATE | ACC_STATIC,
|
||||||
mv.visitCode();
|
mb -> mb.with(ExceptionsAttribute.of(List.of(mb.constantPool().classEntry(CD_IllegalAccessException))))
|
||||||
Label L_illegalAccess = new Label();
|
.withCode(cob -> cob
|
||||||
|
.block(blockBuilder -> blockBuilder
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
.aload(cob.parameterSlot(0))
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "lookupClass",
|
.invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class)
|
||||||
"()Ljava/lang/Class;", false);
|
.constantInstruction(Opcode.LDC, CD_Proxy)
|
||||||
mv.visitLdcInsn(Type.getType(Proxy.class));
|
.if_acmpne(blockBuilder.breakLabel())
|
||||||
mv.visitJumpInsn(IF_ACMPNE, L_illegalAccess);
|
.aload(cob.parameterSlot(0))
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
.invokevirtual(CD_MethodHandles_Lookup, "hasFullPrivilegeAccess", MTD_boolean)
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "hasFullPrivilegeAccess",
|
.ifeq(blockBuilder.breakLabel())
|
||||||
"()Z", false);
|
.invokestatic(CD_MethodHandles, "lookup", MTD_MethodHandles$Lookup)
|
||||||
mv.visitJumpInsn(IFEQ, L_illegalAccess);
|
.areturn())
|
||||||
mv.visitMethodInsn(INVOKESTATIC, JLI_METHODHANDLES, "lookup",
|
.new_(CD_IllegalAccessException)
|
||||||
"()Ljava/lang/invoke/MethodHandles$Lookup;", false);
|
.dup()
|
||||||
mv.visitInsn(ARETURN);
|
.aload(cob.parameterSlot(0))
|
||||||
|
.invokevirtual(CD_MethodHandles_Lookup, "toString", MTD_String)
|
||||||
mv.visitLabel(L_illegalAccess);
|
.invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String)
|
||||||
mv.visitTypeInsn(Opcodes.NEW, JL_ILLEGAL_ACCESS_EX);
|
.athrow()));
|
||||||
mv.visitInsn(DUP);
|
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "toString",
|
|
||||||
"()Ljava/lang/String;", false);
|
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, JL_ILLEGAL_ACCESS_EX,
|
|
||||||
"<init>", "(Ljava/lang/String;)V", false);
|
|
||||||
mv.visitInsn(ATHROW);
|
|
||||||
|
|
||||||
// Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
|
|
||||||
mv.visitMaxs(-1, -1);
|
|
||||||
mv.visitEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -693,12 +716,12 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
private final Class<?> fromClass;
|
private final Class<?> fromClass;
|
||||||
private final Class<?>[] parameterTypes;
|
private final Class<?>[] parameterTypes;
|
||||||
private final Class<?> returnType;
|
private final Class<?> returnType;
|
||||||
private final String methodFieldName;
|
private final Utf8Entry methodFieldName;
|
||||||
private Class<?>[] exceptionTypes;
|
private Class<?>[] exceptionTypes;
|
||||||
|
|
||||||
private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
|
private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
|
||||||
Class<?> returnType, Class<?>[] exceptionTypes,
|
Class<?> returnType, Class<?>[] exceptionTypes,
|
||||||
Class<?> fromClass, String methodFieldName) {
|
Class<?> fromClass, Utf8Entry methodFieldName) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.shortSignature = sig;
|
this.shortSignature = sig;
|
||||||
this.parameterTypes = parameterTypes;
|
this.parameterTypes = parameterTypes;
|
||||||
@ -714,7 +737,7 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
* @param method The method for which to create a proxy
|
* @param method The method for which to create a proxy
|
||||||
* @param methodFieldName the fieldName to generate
|
* @param methodFieldName the fieldName to generate
|
||||||
*/
|
*/
|
||||||
private ProxyMethod(Method method, String methodFieldName) {
|
private ProxyMethod(Method method, Utf8Entry methodFieldName) {
|
||||||
this(method, method.toShortSignature(),
|
this(method, method.toShortSignature(),
|
||||||
method.getSharedParameterTypes(), method.getReturnType(),
|
method.getSharedParameterTypes(), method.getReturnType(),
|
||||||
method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName);
|
method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName);
|
||||||
@ -723,91 +746,61 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
/**
|
/**
|
||||||
* Generate this method, including the code and exception table entry.
|
* Generate this method, including the code and exception table entry.
|
||||||
*/
|
*/
|
||||||
private void generateMethod(ClassWriter cw, String className) {
|
private void generateMethod(ClassBuilder clb, ClassEntry className) {
|
||||||
MethodType mt = MethodType.methodType(returnType, parameterTypes);
|
var cp = clb.constantPool();
|
||||||
String desc = mt.toMethodDescriptorString();
|
MethodTypeDesc desc = MethodTypeDesc.of(toClassDesc(returnType),
|
||||||
int accessFlags = ACC_PUBLIC | ACC_FINAL;
|
Arrays.stream(parameterTypes).map(ProxyGenerator::toClassDesc).toArray(ClassDesc[]::new));
|
||||||
if (method.isVarArgs()) accessFlags |= ACC_VARARGS;
|
int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL
|
||||||
|
: ACC_PUBLIC | ACC_FINAL;
|
||||||
MethodVisitor mv = cw.visitMethod(accessFlags,
|
var catchList = computeUniqueCatchList(exceptionTypes);
|
||||||
method.getName(), desc, null,
|
clb.withMethod(method.getName(), desc, accessFlags, mb ->
|
||||||
typeNames(Arrays.asList(exceptionTypes)));
|
mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes))))
|
||||||
|
.withCode(cob -> {
|
||||||
int[] parameterSlot = new int[parameterTypes.length];
|
cob.aload(cob.receiverSlot())
|
||||||
int nextSlot = 1;
|
.getfield(FRE_Proxy_h)
|
||||||
for (int i = 0; i < parameterSlot.length; i++) {
|
.aload(cob.receiverSlot())
|
||||||
parameterSlot[i] = nextSlot;
|
.getstatic(cp.fieldRefEntry(className, cp.nameAndTypeEntry(methodFieldName, UE_Method)));
|
||||||
nextSlot += getWordsPerType(parameterTypes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
mv.visitCode();
|
|
||||||
Label L_startBlock = new Label();
|
|
||||||
Label L_endBlock = new Label();
|
|
||||||
Label L_RuntimeHandler = new Label();
|
|
||||||
Label L_ThrowableHandler = new Label();
|
|
||||||
|
|
||||||
List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes);
|
|
||||||
if (catchList.size() > 0) {
|
|
||||||
for (Class<?> ex : catchList) {
|
|
||||||
mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler,
|
|
||||||
dotToSlash(ex.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler,
|
|
||||||
JL_THROWABLE);
|
|
||||||
}
|
|
||||||
mv.visitLabel(L_startBlock);
|
|
||||||
|
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
|
||||||
mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,
|
|
||||||
LJLR_INVOCATION_HANDLER);
|
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
|
||||||
mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,
|
|
||||||
LJLR_METHOD);
|
|
||||||
|
|
||||||
if (parameterTypes.length > 0) {
|
if (parameterTypes.length > 0) {
|
||||||
// Create an array and fill with the parameters converting primitives to wrappers
|
// Create an array and fill with the parameters converting primitives to wrappers
|
||||||
emitIconstInsn(mv, parameterTypes.length);
|
cob.constantInstruction(parameterTypes.length)
|
||||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT);
|
.anewarray(CE_Object);
|
||||||
for (int i = 0; i < parameterTypes.length; i++) {
|
for (int i = 0; i < parameterTypes.length; i++) {
|
||||||
mv.visitInsn(DUP);
|
cob.dup()
|
||||||
emitIconstInsn(mv, i);
|
.constantInstruction(i);
|
||||||
codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]);
|
codeWrapArgument(cob, parameterTypes[i], cob.parameterSlot(i));
|
||||||
mv.visitInsn(Opcodes.AASTORE);
|
cob.aastore();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mv.visitInsn(Opcodes.ACONST_NULL);
|
cob.aconst_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
|
cob.invokeinterface(IMRE_InvocationHandler_invoke);
|
||||||
"invoke",
|
|
||||||
"(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
|
|
||||||
"[Ljava/lang/Object;)Ljava/lang/Object;", true);
|
|
||||||
|
|
||||||
if (returnType == void.class) {
|
if (returnType == void.class) {
|
||||||
mv.visitInsn(POP);
|
cob.pop()
|
||||||
mv.visitInsn(RETURN);
|
.return_();
|
||||||
} else {
|
} else {
|
||||||
codeUnwrapReturnValue(mv, returnType);
|
codeUnwrapReturnValue(cob, returnType);
|
||||||
}
|
}
|
||||||
|
if (!catchList.isEmpty()) {
|
||||||
mv.visitLabel(L_endBlock);
|
var c1 = cob.newBoundLabel();
|
||||||
|
for (var exc : catchList) {
|
||||||
// Generate exception handler
|
cob.exceptionCatch(cob.startLabel(), c1, c1, toClassDesc(exc));
|
||||||
mv.visitLabel(L_RuntimeHandler);
|
}
|
||||||
mv.visitInsn(ATHROW); // just rethrow the exception
|
cob.athrow(); // just rethrow the exception
|
||||||
|
var c2 = cob.newBoundLabel();
|
||||||
mv.visitLabel(L_ThrowableHandler);
|
cob.exceptionCatchAll(cob.startLabel(), c1, c2)
|
||||||
mv.visitVarInsn(ASTORE, 1);
|
.new_(CE_UndeclaredThrowableException)
|
||||||
mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX);
|
.dup_x1()
|
||||||
mv.visitInsn(DUP);
|
.swap()
|
||||||
mv.visitVarInsn(ALOAD, 1);
|
.invokespecial(MRE_UndeclaredThrowableException_init)
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX,
|
.athrow()
|
||||||
"<init>", "(Ljava/lang/Throwable;)V", false);
|
.with(StackMapTableAttribute.of(List.of(
|
||||||
mv.visitInsn(ATHROW);
|
StackMapFrameInfo.of(c1, List.of(), THROWABLE_STACK),
|
||||||
// Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
|
StackMapFrameInfo.of(c2, List.of(), THROWABLE_STACK))));
|
||||||
mv.visitMaxs(-1, -1);
|
}
|
||||||
mv.visitEnd();
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -816,15 +809,13 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
* index, in order for it to be passed (as an Object) to the
|
* index, in order for it to be passed (as an Object) to the
|
||||||
* invocation handler's "invoke" method.
|
* invocation handler's "invoke" method.
|
||||||
*/
|
*/
|
||||||
private void codeWrapArgument(MethodVisitor mv, Class<?> type, int slot) {
|
private void codeWrapArgument(CodeBuilder cob, Class<?> type, int slot) {
|
||||||
if (type.isPrimitive()) {
|
if (type.isPrimitive()) {
|
||||||
|
cob.loadInstruction(TypeKind.from(type).asLoadable(), slot);
|
||||||
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
|
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
|
||||||
|
cob.invokestatic(prim.wrapperMethodRef);
|
||||||
mv.visitVarInsn(prim.loadOpcode, slot);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, prim.wrapperClassName, "valueOf",
|
|
||||||
prim.wrapperValueOfDesc, false);
|
|
||||||
} else {
|
} else {
|
||||||
mv.visitVarInsn(ALOAD, slot);
|
cob.aload(slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -833,19 +824,16 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
* type from the invocation handler's "invoke" method (as type
|
* type from the invocation handler's "invoke" method (as type
|
||||||
* Object) to its correct type.
|
* Object) to its correct type.
|
||||||
*/
|
*/
|
||||||
private void codeUnwrapReturnValue(MethodVisitor mv, Class<?> type) {
|
private void codeUnwrapReturnValue(CodeBuilder cob, Class<?> type) {
|
||||||
if (type.isPrimitive()) {
|
if (type.isPrimitive()) {
|
||||||
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
|
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
|
||||||
|
|
||||||
mv.visitTypeInsn(CHECKCAST, prim.wrapperClassName);
|
cob.checkcast(prim.wrapperClass)
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL,
|
.invokevirtual(prim.unwrapMethodRef)
|
||||||
prim.wrapperClassName,
|
.returnInstruction(TypeKind.from(type).asLoadable());
|
||||||
prim.unwrapMethodName, prim.unwrapMethodDesc, false);
|
|
||||||
|
|
||||||
mv.visitInsn(prim.returnOpcode);
|
|
||||||
} else {
|
} else {
|
||||||
mv.visitTypeInsn(CHECKCAST, dotToSlash(type.getName()));
|
cob.checkcast(toClassDesc(type))
|
||||||
mv.visitInsn(ARETURN);
|
.areturn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,40 +842,29 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
* the Method object for this proxy method. A class loader is
|
* the Method object for this proxy method. A class loader is
|
||||||
* anticipated at local variable index 0.
|
* anticipated at local variable index 0.
|
||||||
*/
|
*/
|
||||||
private void codeFieldInitialization(MethodVisitor mv, String className) {
|
private void codeFieldInitialization(CodeBuilder cob, ClassEntry className) {
|
||||||
codeClassForName(mv, fromClass);
|
var cp = cob.constantPool();
|
||||||
|
codeClassForName(cob, fromClass);
|
||||||
|
|
||||||
mv.visitLdcInsn(method.getName());
|
cob.ldc(method.getName())
|
||||||
|
.constantInstruction(parameterTypes.length)
|
||||||
emitIconstInsn(mv, parameterTypes.length);
|
.anewarray(CE_Class);
|
||||||
|
|
||||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS);
|
|
||||||
|
|
||||||
// Construct an array with the parameter types mapping primitives to Wrapper types
|
// Construct an array with the parameter types mapping primitives to Wrapper types
|
||||||
for (int i = 0; i < parameterTypes.length; i++) {
|
for (int i = 0; i < parameterTypes.length; i++) {
|
||||||
mv.visitInsn(DUP);
|
cob.dup()
|
||||||
emitIconstInsn(mv, i);
|
.constantInstruction(i);
|
||||||
|
|
||||||
if (parameterTypes[i].isPrimitive()) {
|
if (parameterTypes[i].isPrimitive()) {
|
||||||
PrimitiveTypeInfo prim =
|
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]);
|
||||||
PrimitiveTypeInfo.get(parameterTypes[i]);
|
cob.getstatic(prim.typeFieldRef);
|
||||||
mv.visitFieldInsn(GETSTATIC,
|
|
||||||
prim.wrapperClassName, "TYPE", LJL_CLASS);
|
|
||||||
} else {
|
} else {
|
||||||
codeClassForName(mv, parameterTypes[i]);
|
codeClassForName(cob, parameterTypes[i]);
|
||||||
}
|
}
|
||||||
mv.visitInsn(Opcodes.AASTORE);
|
cob.aastore();
|
||||||
}
|
}
|
||||||
// lookup the method
|
// lookup the method
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL,
|
cob.invokevirtual(MRE_Class_getMethod)
|
||||||
JL_CLASS,
|
.putstatic(cp.fieldRefEntry(className, cp.nameAndTypeEntry(methodFieldName, UE_Method)));
|
||||||
"getMethod",
|
|
||||||
"(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
|
|
||||||
false);
|
|
||||||
|
|
||||||
mv.visitFieldInsn(PUTSTATIC,
|
|
||||||
dotToSlash(className),
|
|
||||||
methodFieldName, LJLR_METHOD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -901,33 +878,11 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
* may cause the checked ClassNotFoundException to be thrown. A class
|
* may cause the checked ClassNotFoundException to be thrown. A class
|
||||||
* loader is anticipated at local variable index 0.
|
* loader is anticipated at local variable index 0.
|
||||||
*/
|
*/
|
||||||
private void codeClassForName(MethodVisitor mv, Class<?> cl) {
|
private void codeClassForName(CodeBuilder cob, Class<?> cl) {
|
||||||
mv.visitLdcInsn(cl.getName());
|
cob.ldc(cl.getName())
|
||||||
mv.visitInsn(ICONST_0); // false
|
.iconst_0() // false
|
||||||
mv.visitVarInsn(ALOAD, 0); // classLoader
|
.aload(0)// classLoader
|
||||||
mv.visitMethodInsn(INVOKESTATIC,
|
.invokestatic(MRE_Class_forName);
|
||||||
JL_CLASS,
|
|
||||||
"forName",
|
|
||||||
"(Ljava/lang/String;Z" + LJL_CLASSLOADER + ")Ljava/lang/Class;",
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visit a bytecode for a constant.
|
|
||||||
*
|
|
||||||
* @param mv The MethodVisitor
|
|
||||||
* @param cst The constant value
|
|
||||||
*/
|
|
||||||
private void emitIconstInsn(MethodVisitor mv, final int cst) {
|
|
||||||
if (cst >= -1 && cst <= 5) {
|
|
||||||
mv.visitInsn(Opcodes.ICONST_0 + cst);
|
|
||||||
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.BIPUSH, cst);
|
|
||||||
} else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, cst);
|
|
||||||
} else {
|
|
||||||
mv.visitLdcInsn(cst);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -936,61 +891,45 @@ final class ProxyGenerator extends ClassWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ConstantPoolBuilder CP = ConstantPoolBuilder.of();
|
||||||
/**
|
/**
|
||||||
* A PrimitiveTypeInfo object contains bytecode-related information about
|
* A PrimitiveTypeInfo object contains bytecode-related information about
|
||||||
* a primitive type in its instance fields. The struct for a particular
|
* a primitive type in its instance fields. The struct for a particular
|
||||||
* primitive type can be obtained using the static "get" method.
|
* primitive type can be obtained using the static "get" method.
|
||||||
*/
|
*/
|
||||||
private enum PrimitiveTypeInfo {
|
private enum PrimitiveTypeInfo {
|
||||||
BYTE(byte.class, ILOAD, IRETURN),
|
BYTE(byte.class, CD_byte, CD_Byte),
|
||||||
CHAR(char.class, ILOAD, IRETURN),
|
CHAR(char.class, CD_char, CD_Character),
|
||||||
DOUBLE(double.class, DLOAD, DRETURN),
|
DOUBLE(double.class, CD_double, CD_Double),
|
||||||
FLOAT(float.class, FLOAD, FRETURN),
|
FLOAT(float.class, CD_float, CD_Float),
|
||||||
INT(int.class, ILOAD, IRETURN),
|
INT(int.class, CD_int, CD_Integer),
|
||||||
LONG(long.class, LLOAD, LRETURN),
|
LONG(long.class, CD_long, CD_Long),
|
||||||
SHORT(short.class, ILOAD, IRETURN),
|
SHORT(short.class, CD_short, CD_Short),
|
||||||
BOOLEAN(boolean.class, ILOAD, IRETURN);
|
BOOLEAN(boolean.class, CD_boolean, CD_Boolean);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* internal name of corresponding wrapper class
|
* CP entry of corresponding wrapper class
|
||||||
*/
|
*/
|
||||||
private final String wrapperClassName;
|
private final ClassEntry wrapperClass;
|
||||||
/**
|
/**
|
||||||
* method descriptor for wrapper class "valueOf" factory method
|
* CP entry for wrapper class "valueOf" factory method
|
||||||
*/
|
*/
|
||||||
private final String wrapperValueOfDesc;
|
private final MethodRefEntry wrapperMethodRef;
|
||||||
/**
|
/**
|
||||||
* name of wrapper class method for retrieving primitive value
|
* CP entry of wrapper class method for retrieving primitive value
|
||||||
*/
|
*/
|
||||||
private final String unwrapMethodName;
|
private final MethodRefEntry unwrapMethodRef;
|
||||||
/**
|
/**
|
||||||
* descriptor of same method
|
* CP entry of wrapper class TYPE field
|
||||||
*/
|
*/
|
||||||
private final String unwrapMethodDesc;
|
private final FieldRefEntry typeFieldRef;
|
||||||
/**
|
|
||||||
* Load opcode used by this primitive
|
|
||||||
*/
|
|
||||||
private final int loadOpcode;
|
|
||||||
/**
|
|
||||||
* Return opcode used by this primitive
|
|
||||||
*/
|
|
||||||
private final int returnOpcode;
|
|
||||||
|
|
||||||
PrimitiveTypeInfo(Class<?> primitiveClass, int loadOpcode, int returnOpcode) {
|
PrimitiveTypeInfo(Class<?> primitiveClass, ClassDesc baseType, ClassDesc wrapperClass) {
|
||||||
assert primitiveClass.isPrimitive();
|
assert baseType.isPrimitive();
|
||||||
assert returnOpcode - IRETURN == loadOpcode - ILOAD;
|
this.wrapperClass = CP.classEntry(wrapperClass);
|
||||||
|
this.wrapperMethodRef = CP.methodRefEntry(wrapperClass, "valueOf", MethodTypeDesc.of(wrapperClass, baseType));
|
||||||
Wrapper wrapper = Wrapper.forPrimitiveType(primitiveClass);
|
this.unwrapMethodRef = CP.methodRefEntry(wrapperClass, primitiveClass.getName() + "Value", MethodTypeDesc.of(baseType));
|
||||||
// single-char BaseType descriptor (see JVMS section 4.3.2)
|
this.typeFieldRef = CP.fieldRefEntry(wrapperClass, "TYPE", CD_Class);
|
||||||
String baseTypeString = wrapper.basicTypeString();
|
|
||||||
var wrapperType = wrapper.wrapperType();
|
|
||||||
wrapperClassName = dotToSlash(wrapperType.getName());
|
|
||||||
wrapperValueOfDesc =
|
|
||||||
"(" + baseTypeString + ")" + wrapperType.descriptorString();
|
|
||||||
unwrapMethodName = primitiveClass.getName() + "Value";
|
|
||||||
unwrapMethodDesc = "()" + baseTypeString;
|
|
||||||
this.loadOpcode = loadOpcode;
|
|
||||||
this.returnOpcode = returnOpcode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PrimitiveTypeInfo get(Class<?> cl) {
|
public static PrimitiveTypeInfo get(Class<?> cl) {
|
||||||
|
Loading…
Reference in New Issue
Block a user