diff --git a/src/java.base/share/classes/java/lang/reflect/Proxy.java b/src/java.base/share/classes/java/lang/reflect/Proxy.java index 3c37153b875..24c6aaee01c 100644 --- a/src/java.base/share/classes/java/lang/reflect/Proxy.java +++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java @@ -618,6 +618,7 @@ public class Proxy implements java.io.Serializable { private final List> interfaces; private final ProxyClassContext context; ProxyBuilder(ClassLoader loader, List> interfaces) { + Objects.requireNonNull(interfaces); if (!VM.isModuleSystemInited()) { throw new InternalError("Proxy is not supported until " + "module system is fully initialized"); diff --git a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java index 8911ba11903..9637af4b251 100644 --- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java +++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java @@ -25,16 +25,18 @@ package java.lang.reflect; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Label; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.org.objectweb.asm.Type; -import sun.invoke.util.Wrapper; +import java.lang.classfile.*; +import java.lang.classfile.constantpool.*; +import java.lang.classfile.attribute.ExceptionsAttribute; import sun.security.action.GetBooleanAction; 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.Path; import java.util.ArrayList; @@ -43,8 +45,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; - -import static jdk.internal.org.objectweb.asm.Opcodes.*; +import java.util.Objects; +import java.util.function.IntFunction; /** * 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 * "generateProxyClass" method. */ -final class ProxyGenerator extends ClassWriter { - 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"; +final class ProxyGenerator { - private static final String JL_NO_CLASS_DEF_FOUND_ERROR = "java/lang/NoClassDefFoundError"; - private static final String JL_NO_SUCH_METHOD_EX = "java/lang/NoSuchMethodException"; - private static final String JL_NO_SUCH_METHOD_ERROR = "java/lang/NoSuchMethodError"; - private static final String JLI_LOOKUP = "java/lang/invoke/MethodHandles$Lookup"; - private static final String JLI_METHODHANDLES = "java/lang/invoke/MethodHandles"; + private static final ClassDesc + CD_ClassLoader = ClassDesc.ofInternalName("java/lang/ClassLoader"), + CD_ClassNotFoundException = ClassDesc.ofInternalName("java/lang/ClassNotFoundException"), + CD_IllegalAccessException = ClassDesc.ofInternalName("java/lang/IllegalAccessException"), + 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 String JLR_PROXY = "java/lang/reflect/Proxy"; - private static final String JLR_UNDECLARED_THROWABLE_EX = "java/lang/reflect/UndeclaredThrowableException"; + private static final MethodTypeDesc + MTD_boolean = MethodTypeDesc.of(CD_boolean), + 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 = ""; - private static final String NAME_CLINIT = ""; private static final String NAME_LOOKUP_ACCESSOR = "proxyClassLookup"; 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 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 THROWABLE_STACK; + + @SuppressWarnings("unchecked") + private static T entryByIndex(int index) { + return (T) TEMPLATE.constantPool().entryByIndex(index); + } + 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 { - hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), "m0"); - equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), "m1"); - toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), "m2"); + hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), entryByIndex(ei[18])); + equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), entryByIndex(ei[19])); + toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), entryByIndex(ei[20])); } catch (NoSuchMethodException e) { 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 */ - private final String className; + private ClassEntry classEntry; /** * Proxy interfaces @@ -155,9 +255,12 @@ final class ProxyGenerator extends ClassWriter { */ private ProxyGenerator(ClassLoader loader, String className, List> interfaces, int accessFlags) { - super(ClassWriter.COMPUTE_FRAMES); - this.loader = loader; - this.className = className; + this.classfileContext = ClassFile.of( + ClassFile.StackMapsOption.DROP_STACK_MAPS, + ClassFile.ClassHierarchyResolverOption.of( + ClassHierarchyResolver.ofClassLoading(loader).cached())); + this.cp = ConstantPoolBuilder.of(TEMPLATE); + this.classEntry = cp.classEntry(ClassDesc.of(className)); this.interfaces = interfaces; this.accessFlags = accessFlags; } @@ -174,6 +277,7 @@ final class ProxyGenerator extends ClassWriter { final String name, List> interfaces, int accessFlags) { + Objects.requireNonNull(interfaces); ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); final byte[] classFile = gen.generateClassFile(); @@ -185,7 +289,7 @@ final class ProxyGenerator extends ClassWriter { int i = name.lastIndexOf('.'); Path path; if (i > 0) { - Path dir = Path.of(dotToSlash(name.substring(0, i))); + Path dir = Path.of(name.substring(0, i).replace('.', '/')); Files.createDirectories(dir); path = dir.resolve(name.substring(i + 1) + ".class"); } else { @@ -205,20 +309,22 @@ final class ProxyGenerator extends ClassWriter { } /** - * Return an array of the class and interface names from an array of Classes. - * - * @param classes an array of classes or interfaces - * @return the array of class and interface names; or null if classes is - * null or empty + * {@return the entries of the given type} + * @param types the {@code Class} objects, not primitive types nor array types */ - private static String[] typeNames(List> classes) { - if (classes == null || classes.size() == 0) - return null; - int size = classes.size(); - String[] ifaces = new String[size]; - for (int i = 0; i < size; i++) - ifaces[i] = dotToSlash(classes.get(i).getName()); - return ifaces; + private static ClassEntry[] toClassEntries(ConstantPoolBuilder cp, List> types) { + var ces = new ClassEntry[types.size()]; + for (int i = 0; i < ces.length; i++) + ces[i] = cp.classEntry(cp.utf8Entry(types.get(i).getName().replace('.', '/'))); + return ces; + } + + /** + * {@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; } - /** - * 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}. - *

- * 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 * 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 * class file generation process. */ private byte[] generateClassFile() { - visit(CLASSFILE_VERSION, accessFlags, dotToSlash(className), null, - JLR_PROXY, typeNames(interfaces)); - /* * Add proxy methods for the hashCode, equals, * 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 (Method m : intf.getMethods()) { if (!Modifier.isStatic(m.getModifiers())) { - addProxyMethod(m, intf); + addProxyMethod(m, intf, cp); } } } @@ -488,22 +552,23 @@ final class ProxyGenerator extends ClassWriter { checkReturnTypes(sigmethods); } - generateConstructor(); + return classfileContext.build(classEntry, cp, clb -> { + TEMPLATE.forEach(clb); + clb.withFlags(accessFlags); + clb.withInterfaces(toClassEntries(cp, interfaces)); - for (List sigmethods : proxyMethods.values()) { - for (ProxyMethod pm : sigmethods) { - // add static field for the Method object - visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName, - LJLR_METHOD, null, null); + for (List sigmethods : proxyMethods.values()) { + for (ProxyMethod pm : sigmethods) { + // add static field for the Method object + clb.withField(pm.methodFieldName, UE_Method, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); - // Generate code for proxy method - pm.generateMethod(this, className); + // Generate code for proxy method + pm.generateMethod(clb, classEntry); + } } - } - generateStaticInitializer(); - generateLookupAccessor(); - return toByteArray(); + generateStaticInitializer(clb); + }); } /** @@ -519,7 +584,7 @@ final class ProxyGenerator extends ClassWriter { * passed to the invocation handler's "invoke" method for a given * 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[] exceptionTypes = m.getSharedExceptionTypes(); @@ -545,7 +610,7 @@ final class ProxyGenerator extends ClassWriter { } sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType, 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. */ - private void generateConstructor() { - MethodVisitor ctor = visitMethod(Modifier.PUBLIC, NAME_CTOR, - MJLR_INVOCATIONHANDLER, null, null); - ctor.visitParameter(null, 0); - ctor.visitCode(); - ctor.visitVarInsn(ALOAD, 0); - 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(); + private static void generateConstructor(ClassBuilder clb) { + clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob + .aload(cob.receiverSlot()) + .aload(cob.parameterSlot(0)) + .invokespecial(CD_Proxy, INIT_NAME, MTD_void_InvocationHandler) + .return_()); } /** * Generate the static initializer method for the proxy class. */ - private void generateStaticInitializer() { - - 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 - // Class.forName(String, boolean, ClassLoader) calls - mv.visitLdcInsn(Type.getObjectType(dotToSlash(className))); - mv.visitMethodInsn(INVOKEVIRTUAL, JL_CLASS, - "getClassLoader", "()" + LJL_CLASSLOADER, false); - mv.visitVarInsn(ASTORE, 0); - - mv.visitLabel(L_startBlock); - for (List sigmethods : proxyMethods.values()) { - for (ProxyMethod pm : sigmethods) { - pm.codeFieldInitialization(mv, className); + private void generateStaticInitializer(ClassBuilder clb) { + clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> { + // Put ClassLoader at local variable index 0, used by + // Class.forName(String, boolean, ClassLoader) calls + cob.ldc(classEntry) + .invokevirtual(MRE_Class_getClassLoader) + .astore(0); + var ts = cob.newBoundLabel(); + for (List sigmethods : proxyMethods.values()) { + for (ProxyMethod pm : sigmethods) { + pm.codeFieldInitialization(cob, classEntry); + } } - } - mv.visitInsn(RETURN); - mv.visitLabel(L_endBlock); - // Generate exception handler - - mv.visitLabel(L_NoMethodHandler); - mv.visitVarInsn(ASTORE, 1); - mv.visitTypeInsn(Opcodes.NEW, JL_NO_SUCH_METHOD_ERROR); - mv.visitInsn(DUP); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE, - "getMessage", "()Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESPECIAL, JL_NO_SUCH_METHOD_ERROR, - "", "(Ljava/lang/String;)V", false); - mv.visitInsn(ATHROW); - - mv.visitLabel(L_NoClassHandler); - mv.visitVarInsn(ASTORE, 1); - mv.visitTypeInsn(Opcodes.NEW, JL_NO_CLASS_DEF_FOUND_ERROR); - mv.visitInsn(DUP); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE, - "getMessage", "()Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESPECIAL, JL_NO_CLASS_DEF_FOUND_ERROR, - "", "(Ljava/lang/String;)V", false); - mv.visitInsn(ATHROW); - - // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored - mv.visitMaxs(-1, -1); - mv.visitEnd(); + cob.return_(); + var c1 = cob.newBoundLabel(); + cob.exceptionCatch(ts, c1, c1, CE_NoSuchMethodException) + .new_(CE_NoSuchMethodError) + .dup_x1() + .swap() + .invokevirtual(MRE_Throwable_getMessage) + .invokespecial(MRE_NoSuchMethodError_init) + .athrow(); + var c2 = cob.newBoundLabel(); + cob.exceptionCatch(ts, c1, c2, CE_ClassNotFoundException) + .new_(CE_NoClassDefFoundError) + .dup_x1() + .swap() + .invokevirtual(MRE_Throwable_getMessage) + .invokespecial(MRE_NoClassDefFoundError_init) + .athrow() + .with(StackMapTableAttribute.of(List.of( + StackMapFrameInfo.of(c1, List.of(), THROWABLE_STACK), + StackMapFrameInfo.of(c2, List.of(), THROWABLE_STACK)))); + }); } /** @@ -646,39 +680,28 @@ final class ProxyGenerator extends ClassWriter { * on this proxy class if the caller's lookup class is java.lang.reflect.Proxy; * otherwise, IllegalAccessException is thrown */ - private void generateLookupAccessor() { - MethodVisitor mv = visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_LOOKUP_ACCESSOR, - "(Ljava/lang/invoke/MethodHandles$Lookup;)Ljava/lang/invoke/MethodHandles$Lookup;", null, - new String[] { JL_ILLEGAL_ACCESS_EX }); - mv.visitCode(); - Label L_illegalAccess = new Label(); - - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "lookupClass", - "()Ljava/lang/Class;", false); - mv.visitLdcInsn(Type.getType(Proxy.class)); - mv.visitJumpInsn(IF_ACMPNE, L_illegalAccess); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "hasFullPrivilegeAccess", - "()Z", false); - mv.visitJumpInsn(IFEQ, L_illegalAccess); - mv.visitMethodInsn(INVOKESTATIC, JLI_METHODHANDLES, "lookup", - "()Ljava/lang/invoke/MethodHandles$Lookup;", false); - mv.visitInsn(ARETURN); - - mv.visitLabel(L_illegalAccess); - mv.visitTypeInsn(Opcodes.NEW, JL_ILLEGAL_ACCESS_EX); - mv.visitInsn(DUP); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "toString", - "()Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESPECIAL, JL_ILLEGAL_ACCESS_EX, - "", "(Ljava/lang/String;)V", false); - mv.visitInsn(ATHROW); - - // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored - mv.visitMaxs(-1, -1); - mv.visitEnd(); + private static void generateLookupAccessor(ClassBuilder clb) { + clb.withMethod(NAME_LOOKUP_ACCESSOR, + MTD_MethodHandles$Lookup_MethodHandles$Lookup, + ACC_PRIVATE | ACC_STATIC, + mb -> mb.with(ExceptionsAttribute.of(List.of(mb.constantPool().classEntry(CD_IllegalAccessException)))) + .withCode(cob -> cob + .block(blockBuilder -> blockBuilder + .aload(cob.parameterSlot(0)) + .invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class) + .constantInstruction(Opcode.LDC, CD_Proxy) + .if_acmpne(blockBuilder.breakLabel()) + .aload(cob.parameterSlot(0)) + .invokevirtual(CD_MethodHandles_Lookup, "hasFullPrivilegeAccess", MTD_boolean) + .ifeq(blockBuilder.breakLabel()) + .invokestatic(CD_MethodHandles, "lookup", MTD_MethodHandles$Lookup) + .areturn()) + .new_(CD_IllegalAccessException) + .dup() + .aload(cob.parameterSlot(0)) + .invokevirtual(CD_MethodHandles_Lookup, "toString", MTD_String) + .invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String) + .athrow())); } /** @@ -693,12 +716,12 @@ final class ProxyGenerator extends ClassWriter { private final Class fromClass; private final Class[] parameterTypes; private final Class returnType; - private final String methodFieldName; + private final Utf8Entry methodFieldName; private Class[] exceptionTypes; private ProxyMethod(Method method, String sig, Class[] parameterTypes, Class returnType, Class[] exceptionTypes, - Class fromClass, String methodFieldName) { + Class fromClass, Utf8Entry methodFieldName) { this.method = method; this.shortSignature = sig; this.parameterTypes = parameterTypes; @@ -714,100 +737,70 @@ final class ProxyGenerator extends ClassWriter { * @param method The method for which to create a proxy * @param methodFieldName the fieldName to generate */ - private ProxyMethod(Method method, String methodFieldName) { + private ProxyMethod(Method method, Utf8Entry methodFieldName) { this(method, method.toShortSignature(), - method.getSharedParameterTypes(), method.getReturnType(), - method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName); + method.getSharedParameterTypes(), method.getReturnType(), + method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName); } /** * Generate this method, including the code and exception table entry. */ - private void generateMethod(ClassWriter cw, String className) { - MethodType mt = MethodType.methodType(returnType, parameterTypes); - String desc = mt.toMethodDescriptorString(); - int accessFlags = ACC_PUBLIC | ACC_FINAL; - if (method.isVarArgs()) accessFlags |= ACC_VARARGS; + private void generateMethod(ClassBuilder clb, ClassEntry className) { + var cp = clb.constantPool(); + MethodTypeDesc desc = MethodTypeDesc.of(toClassDesc(returnType), + Arrays.stream(parameterTypes).map(ProxyGenerator::toClassDesc).toArray(ClassDesc[]::new)); + int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL + : ACC_PUBLIC | ACC_FINAL; + var catchList = computeUniqueCatchList(exceptionTypes); + clb.withMethod(method.getName(), desc, accessFlags, mb -> + mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes)))) + .withCode(cob -> { + cob.aload(cob.receiverSlot()) + .getfield(FRE_Proxy_h) + .aload(cob.receiverSlot()) + .getstatic(cp.fieldRefEntry(className, cp.nameAndTypeEntry(methodFieldName, UE_Method))); - MethodVisitor mv = cw.visitMethod(accessFlags, - method.getName(), desc, null, - typeNames(Arrays.asList(exceptionTypes))); + if (parameterTypes.length > 0) { + // Create an array and fill with the parameters converting primitives to wrappers + cob.constantInstruction(parameterTypes.length) + .anewarray(CE_Object); + for (int i = 0; i < parameterTypes.length; i++) { + cob.dup() + .constantInstruction(i); + codeWrapArgument(cob, parameterTypes[i], cob.parameterSlot(i)); + cob.aastore(); + } + } else { + cob.aconst_null(); + } - int[] parameterSlot = new int[parameterTypes.length]; - int nextSlot = 1; - for (int i = 0; i < parameterSlot.length; i++) { - parameterSlot[i] = nextSlot; - nextSlot += getWordsPerType(parameterTypes[i]); - } + cob.invokeinterface(IMRE_InvocationHandler_invoke); - mv.visitCode(); - Label L_startBlock = new Label(); - Label L_endBlock = new Label(); - Label L_RuntimeHandler = new Label(); - Label L_ThrowableHandler = new Label(); - - List> 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) { - // Create an array and fill with the parameters converting primitives to wrappers - emitIconstInsn(mv, parameterTypes.length); - mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT); - for (int i = 0; i < parameterTypes.length; i++) { - mv.visitInsn(DUP); - emitIconstInsn(mv, i); - codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]); - mv.visitInsn(Opcodes.AASTORE); - } - } else { - mv.visitInsn(Opcodes.ACONST_NULL); - } - - mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER, - "invoke", - "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + - "[Ljava/lang/Object;)Ljava/lang/Object;", true); - - if (returnType == void.class) { - mv.visitInsn(POP); - mv.visitInsn(RETURN); - } else { - codeUnwrapReturnValue(mv, returnType); - } - - mv.visitLabel(L_endBlock); - - // Generate exception handler - mv.visitLabel(L_RuntimeHandler); - mv.visitInsn(ATHROW); // just rethrow the exception - - mv.visitLabel(L_ThrowableHandler); - mv.visitVarInsn(ASTORE, 1); - mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX); - mv.visitInsn(DUP); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX, - "", "(Ljava/lang/Throwable;)V", false); - mv.visitInsn(ATHROW); - // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored - mv.visitMaxs(-1, -1); - mv.visitEnd(); + if (returnType == void.class) { + cob.pop() + .return_(); + } else { + codeUnwrapReturnValue(cob, returnType); + } + if (!catchList.isEmpty()) { + var c1 = cob.newBoundLabel(); + for (var exc : catchList) { + cob.exceptionCatch(cob.startLabel(), c1, c1, toClassDesc(exc)); + } + cob.athrow(); // just rethrow the exception + var c2 = cob.newBoundLabel(); + cob.exceptionCatchAll(cob.startLabel(), c1, c2) + .new_(CE_UndeclaredThrowableException) + .dup_x1() + .swap() + .invokespecial(MRE_UndeclaredThrowableException_init) + .athrow() + .with(StackMapTableAttribute.of(List.of( + StackMapFrameInfo.of(c1, List.of(), THROWABLE_STACK), + StackMapFrameInfo.of(c2, List.of(), THROWABLE_STACK)))); + } + })); } /** @@ -816,15 +809,13 @@ final class ProxyGenerator extends ClassWriter { * index, in order for it to be passed (as an Object) to the * 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()) { + cob.loadInstruction(TypeKind.from(type).asLoadable(), slot); PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); - - mv.visitVarInsn(prim.loadOpcode, slot); - mv.visitMethodInsn(INVOKESTATIC, prim.wrapperClassName, "valueOf", - prim.wrapperValueOfDesc, false); + cob.invokestatic(prim.wrapperMethodRef); } 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 * Object) to its correct type. */ - private void codeUnwrapReturnValue(MethodVisitor mv, Class type) { + private void codeUnwrapReturnValue(CodeBuilder cob, Class type) { if (type.isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); - mv.visitTypeInsn(CHECKCAST, prim.wrapperClassName); - mv.visitMethodInsn(INVOKEVIRTUAL, - prim.wrapperClassName, - prim.unwrapMethodName, prim.unwrapMethodDesc, false); - - mv.visitInsn(prim.returnOpcode); + cob.checkcast(prim.wrapperClass) + .invokevirtual(prim.unwrapMethodRef) + .returnInstruction(TypeKind.from(type).asLoadable()); } else { - mv.visitTypeInsn(CHECKCAST, dotToSlash(type.getName())); - mv.visitInsn(ARETURN); + cob.checkcast(toClassDesc(type)) + .areturn(); } } @@ -854,40 +842,29 @@ final class ProxyGenerator extends ClassWriter { * the Method object for this proxy method. A class loader is * anticipated at local variable index 0. */ - private void codeFieldInitialization(MethodVisitor mv, String className) { - codeClassForName(mv, fromClass); + private void codeFieldInitialization(CodeBuilder cob, ClassEntry className) { + var cp = cob.constantPool(); + codeClassForName(cob, fromClass); - mv.visitLdcInsn(method.getName()); - - emitIconstInsn(mv, parameterTypes.length); - - mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS); + cob.ldc(method.getName()) + .constantInstruction(parameterTypes.length) + .anewarray(CE_Class); // Construct an array with the parameter types mapping primitives to Wrapper types for (int i = 0; i < parameterTypes.length; i++) { - mv.visitInsn(DUP); - emitIconstInsn(mv, i); - + cob.dup() + .constantInstruction(i); if (parameterTypes[i].isPrimitive()) { - PrimitiveTypeInfo prim = - PrimitiveTypeInfo.get(parameterTypes[i]); - mv.visitFieldInsn(GETSTATIC, - prim.wrapperClassName, "TYPE", LJL_CLASS); + PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]); + cob.getstatic(prim.typeFieldRef); } else { - codeClassForName(mv, parameterTypes[i]); + codeClassForName(cob, parameterTypes[i]); } - mv.visitInsn(Opcodes.AASTORE); + cob.aastore(); } // lookup the method - mv.visitMethodInsn(INVOKEVIRTUAL, - JL_CLASS, - "getMethod", - "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", - false); - - mv.visitFieldInsn(PUTSTATIC, - dotToSlash(className), - methodFieldName, LJLR_METHOD); + cob.invokevirtual(MRE_Class_getMethod) + .putstatic(cp.fieldRefEntry(className, cp.nameAndTypeEntry(methodFieldName, UE_Method))); } /* @@ -901,33 +878,11 @@ final class ProxyGenerator extends ClassWriter { * may cause the checked ClassNotFoundException to be thrown. A class * loader is anticipated at local variable index 0. */ - private void codeClassForName(MethodVisitor mv, Class cl) { - mv.visitLdcInsn(cl.getName()); - mv.visitInsn(ICONST_0); // false - mv.visitVarInsn(ALOAD, 0); // classLoader - mv.visitMethodInsn(INVOKESTATIC, - 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); - } + private void codeClassForName(CodeBuilder cob, Class cl) { + cob.ldc(cl.getName()) + .iconst_0() // false + .aload(0)// classLoader + .invokestatic(MRE_Class_forName); } @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 primitive type in its instance fields. The struct for a particular * primitive type can be obtained using the static "get" method. */ private enum PrimitiveTypeInfo { - BYTE(byte.class, ILOAD, IRETURN), - CHAR(char.class, ILOAD, IRETURN), - DOUBLE(double.class, DLOAD, DRETURN), - FLOAT(float.class, FLOAD, FRETURN), - INT(int.class, ILOAD, IRETURN), - LONG(long.class, LLOAD, LRETURN), - SHORT(short.class, ILOAD, IRETURN), - BOOLEAN(boolean.class, ILOAD, IRETURN); + BYTE(byte.class, CD_byte, CD_Byte), + CHAR(char.class, CD_char, CD_Character), + DOUBLE(double.class, CD_double, CD_Double), + FLOAT(float.class, CD_float, CD_Float), + INT(int.class, CD_int, CD_Integer), + LONG(long.class, CD_long, CD_Long), + SHORT(short.class, CD_short, CD_Short), + 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; - /** - * Load opcode used by this primitive - */ - private final int loadOpcode; - /** - * Return opcode used by this primitive - */ - private final int returnOpcode; + private final FieldRefEntry typeFieldRef; - PrimitiveTypeInfo(Class primitiveClass, int loadOpcode, int returnOpcode) { - assert primitiveClass.isPrimitive(); - assert returnOpcode - IRETURN == loadOpcode - ILOAD; - - Wrapper wrapper = Wrapper.forPrimitiveType(primitiveClass); - // single-char BaseType descriptor (see JVMS section 4.3.2) - 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; + PrimitiveTypeInfo(Class primitiveClass, ClassDesc baseType, ClassDesc wrapperClass) { + assert baseType.isPrimitive(); + this.wrapperClass = CP.classEntry(wrapperClass); + this.wrapperMethodRef = CP.methodRefEntry(wrapperClass, "valueOf", MethodTypeDesc.of(wrapperClass, baseType)); + this.unwrapMethodRef = CP.methodRefEntry(wrapperClass, primitiveClass.getName() + "Value", MethodTypeDesc.of(baseType)); + this.typeFieldRef = CP.fieldRefEntry(wrapperClass, "TYPE", CD_Class); } public static PrimitiveTypeInfo get(Class cl) {