--- old/src/java.base/share/classes/java/lang/reflect/Method.java 2019-08-21 16:28:41.616024331 -0400 +++ new/src/java.base/share/classes/java/lang/reflect/Method.java 2019-08-21 16:28:41.312022824 -0400 @@ -420,17 +420,16 @@ @Override String toShortString() { - StringBuilder sb = new StringBuilder("method "); - sb.append(getDeclaringClass().getTypeName()).append('.'); - sb.append(getName()); - sb.append('('); - StringJoiner sj = new StringJoiner(","); + return "method " + getDeclaringClass().getTypeName() + + '.' + toShortSignature(); + } + + String toShortSignature() { + StringJoiner sj = new StringJoiner(",", getName() + "(", ")"); for (Class parameterType : getParameterTypes()) { sj.add(parameterType.getTypeName()); } - sb.append(sj); - sb.append(')'); - return sb.toString(); + return sj.toString(); } /** --- old/src/java.base/share/classes/java/lang/reflect/Proxy.java 2019-08-21 16:28:42.140026929 -0400 +++ new/src/java.base/share/classes/java/lang/reflect/Proxy.java 2019-08-21 16:28:41.836025422 -0400 @@ -49,6 +49,7 @@ import jdk.internal.reflect.Reflection; import jdk.internal.loader.ClassLoaderValue; import sun.reflect.misc.ReflectUtil; +import sun.security.action.GetBooleanAction; import sun.security.action.GetPropertyAction; import sun.security.util.SecurityConstants; @@ -99,7 +100,7 @@ *
  • A proxy class extends {@code java.lang.reflect.Proxy}. * *
  • A proxy class implements exactly the interfaces specified at its - * creation, in the same order. Invoking {@link Class#getInterfaces getInterfaces} + * creation, in the same order. Invoking {@link Class#getInterfaces() getInterfaces} * on its {@code Class} object will return an array containing the same * list of interfaces (in the order specified at its creation), invoking * {@link Class#getMethods getMethods} on its {@code Class} object will return @@ -296,6 +297,13 @@ new ClassLoaderValue<>(); /** + * System property to revert to generation of proxy class files for version 1.5 (V49). + * Set to "true" to generate v49 class file format. + */ + private static final boolean PROXY_GENERATOR_V49 = + GetBooleanAction.privilegedGetProperty("jdk.proxy.ProxyGenerator.v49"); + + /** * the invocation handler for this proxy instance. * @serial */ @@ -531,8 +539,9 @@ /* * Generate the specified proxy class. */ - byte[] proxyClassFile = ProxyGenerator.generateProxyClass( - proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags); + byte[] proxyClassFile = PROXY_GENERATOR_V49 + ? ProxyGenerator_v49.generateProxyClass(proxyName, interfaces, accessFlags) + : ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags); try { Class pc = JLA.defineClass(loader, proxyName, proxyClassFile, null, "__dynamic_proxy__"); @@ -1116,6 +1125,5 @@ return ih; } - private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; private static final String PROXY_PACKAGE_PREFIX = ReflectUtil.PROXY_PACKAGE; } --- old/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java 2019-08-21 16:28:42.668029548 -0400 +++ new/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java 2019-08-21 16:28:42.376028100 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,599 +25,191 @@ package java.lang.reflect; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; +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 sun.security.action.GetBooleanAction; + import java.io.IOException; -import java.io.OutputStream; -import java.lang.reflect.Array; -import java.lang.reflect.Method; +import java.lang.invoke.MethodType; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; -import sun.security.action.GetBooleanAction; + +import static jdk.internal.org.objectweb.asm.Opcodes.*; /** * ProxyGenerator contains the code to generate a dynamic proxy class * for the java.lang.reflect.Proxy API. - * - * The external interfaces to ProxyGenerator is the static + *

    + * The external interface to ProxyGenerator is the static * "generateProxyClass" method. - * - * @author Peter Jones - * @since 1.3 */ -class ProxyGenerator { - /* - * In the comments below, "JVMS" refers to The Java Virtual Machine - * Specification Second Edition and "JLS" refers to the original - * version of The Java Language Specification, unless otherwise - * specified. - */ - - /* generate 1.5-era class file version */ - private static final int CLASSFILE_MAJOR_VERSION = 49; - private static final int CLASSFILE_MINOR_VERSION = 0; - - /* - * beginning of constants copied from - * sun.tools.java.RuntimeConstants (which no longer exists): - */ - - /* constant pool tags */ - private static final int CONSTANT_UTF8 = 1; - private static final int CONSTANT_UNICODE = 2; - private static final int CONSTANT_INTEGER = 3; - private static final int CONSTANT_FLOAT = 4; - private static final int CONSTANT_LONG = 5; - private static final int CONSTANT_DOUBLE = 6; - private static final int CONSTANT_CLASS = 7; - private static final int CONSTANT_STRING = 8; - private static final int CONSTANT_FIELD = 9; - private static final int CONSTANT_METHOD = 10; - private static final int CONSTANT_INTERFACEMETHOD = 11; - private static final int CONSTANT_NAMEANDTYPE = 12; - - /* access and modifier flags */ - private static final int ACC_PUBLIC = 0x00000001; - private static final int ACC_PRIVATE = 0x00000002; -// private static final int ACC_PROTECTED = 0x00000004; - private static final int ACC_STATIC = 0x00000008; - private static final int ACC_FINAL = 0x00000010; -// private static final int ACC_SYNCHRONIZED = 0x00000020; -// private static final int ACC_VOLATILE = 0x00000040; -// private static final int ACC_TRANSIENT = 0x00000080; -// private static final int ACC_NATIVE = 0x00000100; -// private static final int ACC_INTERFACE = 0x00000200; -// private static final int ACC_ABSTRACT = 0x00000400; - private static final int ACC_SUPER = 0x00000020; -// private static final int ACC_STRICT = 0x00000800; - - /* opcodes */ -// private static final int opc_nop = 0; - private static final int opc_aconst_null = 1; -// private static final int opc_iconst_m1 = 2; - private static final int opc_iconst_0 = 3; -// private static final int opc_iconst_1 = 4; -// private static final int opc_iconst_2 = 5; -// private static final int opc_iconst_3 = 6; -// private static final int opc_iconst_4 = 7; -// private static final int opc_iconst_5 = 8; -// private static final int opc_lconst_0 = 9; -// private static final int opc_lconst_1 = 10; -// private static final int opc_fconst_0 = 11; -// private static final int opc_fconst_1 = 12; -// private static final int opc_fconst_2 = 13; -// private static final int opc_dconst_0 = 14; -// private static final int opc_dconst_1 = 15; - private static final int opc_bipush = 16; - private static final int opc_sipush = 17; - private static final int opc_ldc = 18; - private static final int opc_ldc_w = 19; -// private static final int opc_ldc2_w = 20; - private static final int opc_iload = 21; - private static final int opc_lload = 22; - private static final int opc_fload = 23; - private static final int opc_dload = 24; - private static final int opc_aload = 25; - private static final int opc_iload_0 = 26; -// private static final int opc_iload_1 = 27; -// private static final int opc_iload_2 = 28; -// private static final int opc_iload_3 = 29; - private static final int opc_lload_0 = 30; -// private static final int opc_lload_1 = 31; -// private static final int opc_lload_2 = 32; -// private static final int opc_lload_3 = 33; - private static final int opc_fload_0 = 34; -// private static final int opc_fload_1 = 35; -// private static final int opc_fload_2 = 36; -// private static final int opc_fload_3 = 37; - private static final int opc_dload_0 = 38; -// private static final int opc_dload_1 = 39; -// private static final int opc_dload_2 = 40; -// private static final int opc_dload_3 = 41; - private static final int opc_aload_0 = 42; -// private static final int opc_aload_1 = 43; -// private static final int opc_aload_2 = 44; -// private static final int opc_aload_3 = 45; -// private static final int opc_iaload = 46; -// private static final int opc_laload = 47; -// private static final int opc_faload = 48; -// private static final int opc_daload = 49; -// private static final int opc_aaload = 50; -// private static final int opc_baload = 51; -// private static final int opc_caload = 52; -// private static final int opc_saload = 53; -// private static final int opc_istore = 54; -// private static final int opc_lstore = 55; -// private static final int opc_fstore = 56; -// private static final int opc_dstore = 57; - private static final int opc_astore = 58; -// private static final int opc_istore_0 = 59; -// private static final int opc_istore_1 = 60; -// private static final int opc_istore_2 = 61; -// private static final int opc_istore_3 = 62; -// private static final int opc_lstore_0 = 63; -// private static final int opc_lstore_1 = 64; -// private static final int opc_lstore_2 = 65; -// private static final int opc_lstore_3 = 66; -// private static final int opc_fstore_0 = 67; -// private static final int opc_fstore_1 = 68; -// private static final int opc_fstore_2 = 69; -// private static final int opc_fstore_3 = 70; -// private static final int opc_dstore_0 = 71; -// private static final int opc_dstore_1 = 72; -// private static final int opc_dstore_2 = 73; -// private static final int opc_dstore_3 = 74; - private static final int opc_astore_0 = 75; -// private static final int opc_astore_1 = 76; -// private static final int opc_astore_2 = 77; -// private static final int opc_astore_3 = 78; -// private static final int opc_iastore = 79; -// private static final int opc_lastore = 80; -// private static final int opc_fastore = 81; -// private static final int opc_dastore = 82; - private static final int opc_aastore = 83; -// private static final int opc_bastore = 84; -// private static final int opc_castore = 85; -// private static final int opc_sastore = 86; - private static final int opc_pop = 87; -// private static final int opc_pop2 = 88; - private static final int opc_dup = 89; -// private static final int opc_dup_x1 = 90; -// private static final int opc_dup_x2 = 91; -// private static final int opc_dup2 = 92; -// private static final int opc_dup2_x1 = 93; -// private static final int opc_dup2_x2 = 94; -// private static final int opc_swap = 95; -// private static final int opc_iadd = 96; -// private static final int opc_ladd = 97; -// private static final int opc_fadd = 98; -// private static final int opc_dadd = 99; -// private static final int opc_isub = 100; -// private static final int opc_lsub = 101; -// private static final int opc_fsub = 102; -// private static final int opc_dsub = 103; -// private static final int opc_imul = 104; -// private static final int opc_lmul = 105; -// private static final int opc_fmul = 106; -// private static final int opc_dmul = 107; -// private static final int opc_idiv = 108; -// private static final int opc_ldiv = 109; -// private static final int opc_fdiv = 110; -// private static final int opc_ddiv = 111; -// private static final int opc_irem = 112; -// private static final int opc_lrem = 113; -// private static final int opc_frem = 114; -// private static final int opc_drem = 115; -// private static final int opc_ineg = 116; -// private static final int opc_lneg = 117; -// private static final int opc_fneg = 118; -// private static final int opc_dneg = 119; -// private static final int opc_ishl = 120; -// private static final int opc_lshl = 121; -// private static final int opc_ishr = 122; -// private static final int opc_lshr = 123; -// private static final int opc_iushr = 124; -// private static final int opc_lushr = 125; -// private static final int opc_iand = 126; -// private static final int opc_land = 127; -// private static final int opc_ior = 128; -// private static final int opc_lor = 129; -// private static final int opc_ixor = 130; -// private static final int opc_lxor = 131; -// private static final int opc_iinc = 132; -// private static final int opc_i2l = 133; -// private static final int opc_i2f = 134; -// private static final int opc_i2d = 135; -// private static final int opc_l2i = 136; -// private static final int opc_l2f = 137; -// private static final int opc_l2d = 138; -// private static final int opc_f2i = 139; -// private static final int opc_f2l = 140; -// private static final int opc_f2d = 141; -// private static final int opc_d2i = 142; -// private static final int opc_d2l = 143; -// private static final int opc_d2f = 144; -// private static final int opc_i2b = 145; -// private static final int opc_i2c = 146; -// private static final int opc_i2s = 147; -// private static final int opc_lcmp = 148; -// private static final int opc_fcmpl = 149; -// private static final int opc_fcmpg = 150; -// private static final int opc_dcmpl = 151; -// private static final int opc_dcmpg = 152; -// private static final int opc_ifeq = 153; -// private static final int opc_ifne = 154; -// private static final int opc_iflt = 155; -// private static final int opc_ifge = 156; -// private static final int opc_ifgt = 157; -// private static final int opc_ifle = 158; -// private static final int opc_if_icmpeq = 159; -// private static final int opc_if_icmpne = 160; -// private static final int opc_if_icmplt = 161; -// private static final int opc_if_icmpge = 162; -// private static final int opc_if_icmpgt = 163; -// private static final int opc_if_icmple = 164; -// private static final int opc_if_acmpeq = 165; -// private static final int opc_if_acmpne = 166; -// private static final int opc_goto = 167; -// private static final int opc_jsr = 168; -// private static final int opc_ret = 169; -// private static final int opc_tableswitch = 170; -// private static final int opc_lookupswitch = 171; - private static final int opc_ireturn = 172; - private static final int opc_lreturn = 173; - private static final int opc_freturn = 174; - private static final int opc_dreturn = 175; - private static final int opc_areturn = 176; - private static final int opc_return = 177; - private static final int opc_getstatic = 178; - private static final int opc_putstatic = 179; - private static final int opc_getfield = 180; -// private static final int opc_putfield = 181; - private static final int opc_invokevirtual = 182; - private static final int opc_invokespecial = 183; - private static final int opc_invokestatic = 184; - private static final int opc_invokeinterface = 185; - private static final int opc_new = 187; -// private static final int opc_newarray = 188; - private static final int opc_anewarray = 189; -// private static final int opc_arraylength = 190; - private static final int opc_athrow = 191; - private static final int opc_checkcast = 192; -// private static final int opc_instanceof = 193; -// private static final int opc_monitorenter = 194; -// private static final int opc_monitorexit = 195; - private static final int opc_wide = 196; -// private static final int opc_multianewarray = 197; -// private static final int opc_ifnull = 198; -// private static final int opc_ifnonnull = 199; -// private static final int opc_goto_w = 200; -// private static final int opc_jsr_w = 201; +final class ProxyGenerator extends ClassWriter { - // end of constants copied from sun.tools.java.RuntimeConstants + 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_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"; - /** name of the superclass of proxy classes */ - private static final String superclassName = "java/lang/reflect/Proxy"; + 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"; - /** name of field for storing a proxy instance's invocation handler */ - private static final String handlerFieldName = "h"; + private static final String LJL_CLASS = "Ljava/lang/Class;"; + private static final String LJLR_METHOD = "Ljava/lang/reflect/Method;"; + private static final String LJLR_INVOCATION_HANDLER = "Ljava/lang/reflect/InvocationHandler;"; - /** debugging flag for saving generated class files */ - private static final boolean saveGeneratedFiles = - java.security.AccessController.doPrivileged( - new GetBooleanAction( - "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue(); + 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 Class[] EMPTY_CLASS_ARRAY = new Class[0]; /** - * Generate a public proxy class given a name and a list of proxy interfaces. + * name of field for storing a proxy instance's invocation handler */ - static byte[] generateProxyClass(final String name, - Class[] interfaces) { - return generateProxyClass(name, interfaces, (ACC_PUBLIC | ACC_FINAL | ACC_SUPER)); - } + private static final String handlerFieldName = "h"; /** - * Generate a proxy class given a name and a list of proxy interfaces. - * - * @param name the class name of the proxy class - * @param interfaces proxy interfaces - * @param accessFlags access flags of the proxy class - */ - static byte[] generateProxyClass(final String name, - Class[] interfaces, - int accessFlags) - { - ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); - final byte[] classFile = gen.generateClassFile(); - - if (saveGeneratedFiles) { + * debugging flag for saving generated class files + */ + private static final boolean saveGeneratedFiles = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Void run() { - try { - int i = name.lastIndexOf('.'); - Path path; - if (i > 0) { - Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar)); - Files.createDirectories(dir); - path = dir.resolve(name.substring(i+1, name.length()) + ".class"); - } else { - path = Path.of(name + ".class"); - } - Files.write(path, classFile); - return null; - } catch (IOException e) { - throw new InternalError( - "I/O exception saving generated file: " + e); - } - } - }); - } + new GetBooleanAction( + "jdk.proxy.ProxyGenerator.saveGeneratedFiles")); - return classFile; - } + /* Preloaded ProxyMethod objects for methods in java.lang.Object */ + private final static ProxyMethod hashCodeMethod; + private final static ProxyMethod equalsMethod; + private final static ProxyMethod toStringMethod; - /* preloaded Method objects for methods in java.lang.Object */ - private static Method hashCodeMethod; - private static Method equalsMethod; - private static Method toStringMethod; static { try { - hashCodeMethod = Object.class.getMethod("hashCode"); - equalsMethod = - Object.class.getMethod("equals", new Class[] { Object.class }); - toStringMethod = Object.class.getMethod("toString"); + 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"); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } - /** name of proxy class */ - private String className; - - /** proxy interfaces */ - private Class[] interfaces; - - /** proxy class access flags */ - private int accessFlags; + /** + * Class loader + */ + private final ClassLoader loader; - /** constant pool of class being generated */ - private ConstantPool cp = new ConstantPool(); + /** + * Name of proxy class + */ + private final String className; - /** FieldInfo struct for each field of generated class */ - private List fields = new ArrayList<>(); + /** + * Proxy interfaces + */ + private final List> interfaces; - /** MethodInfo struct for each method of generated class */ - private List methods = new ArrayList<>(); + /** + * Proxy class access flags + */ + private final int accessFlags; /** - * maps method signature string to list of ProxyMethod objects for - * proxy methods with that signature + * Maps method signature string to list of ProxyMethod objects for + * proxy methods with that signature. + * Kept in insertion order to make it easier to compare old and new. */ - private Map> proxyMethods = new HashMap<>(); + private final Map> proxyMethods = new LinkedHashMap<>(); - /** count of ProxyMethod objects added to proxyMethods */ - private int proxyMethodCount = 0; + /** + * Ordinal of next ProxyMethod object added to proxyMethods. + * Indexes are reserved for hashcode(0), equals(1), toString(2). + */ + private int proxyMethodCount = 3; /** * Construct a ProxyGenerator to generate a proxy class with the * specified name and for the given interfaces. - * + *

    * A ProxyGenerator object contains the state for the ongoing * generation of a particular proxy class. */ - private ProxyGenerator(String className, Class[] interfaces, int accessFlags) { + private ProxyGenerator(ClassLoader loader, String className, List> interfaces, + int accessFlags) { + super(ClassWriter.COMPUTE_FRAMES); + this.loader = loader; this.className = className; this.interfaces = interfaces; this.accessFlags = accessFlags; } /** - * Generate a class file for the proxy class. This method drives the - * class file generation process. + * Generate a proxy class given a name and a list of proxy interfaces. + * + * @param name the class name of the proxy class + * @param interfaces proxy interfaces + * @param accessFlags access flags of the proxy class */ - private byte[] generateClassFile() { - - /* ============================================================ - * Step 1: Assemble ProxyMethod objects for all methods to - * generate proxy dispatching code for. - */ - - /* - * Record that proxy methods are needed for the hashCode, equals, - * and toString methods of java.lang.Object. This is done before - * the methods from the proxy interfaces so that the methods from - * java.lang.Object take precedence over duplicate methods in the - * proxy interfaces. - */ - addProxyMethod(hashCodeMethod, Object.class); - addProxyMethod(equalsMethod, Object.class); - addProxyMethod(toStringMethod, Object.class); - - /* - * Now record all of the methods from the proxy interfaces, giving - * earlier interfaces precedence over later ones with duplicate - * methods. - */ - for (Class intf : interfaces) { - for (Method m : intf.getMethods()) { - if (!Modifier.isStatic(m.getModifiers())) { - addProxyMethod(m, intf); - } - } - } - - /* - * For each set of proxy methods with the same signature, - * verify that the methods' return types are compatible. - */ - for (List sigmethods : proxyMethods.values()) { - checkReturnTypes(sigmethods); - } - - /* ============================================================ - * Step 2: Assemble FieldInfo and MethodInfo structs for all of - * fields and methods in the class we are generating. - */ - try { - methods.add(generateConstructor()); - - for (List sigmethods : proxyMethods.values()) { - for (ProxyMethod pm : sigmethods) { - - // add static field for method's Method object - fields.add(new FieldInfo(pm.methodFieldName, - "Ljava/lang/reflect/Method;", - ACC_PRIVATE | ACC_STATIC)); - - // generate code for proxy method and add it - methods.add(pm.generateMethod()); - } - } - - methods.add(generateStaticInitializer()); - - } catch (IOException e) { - throw new InternalError("unexpected I/O Exception", e); - } - - if (methods.size() > 65535) { - throw new IllegalArgumentException("method limit exceeded"); - } - if (fields.size() > 65535) { - throw new IllegalArgumentException("field limit exceeded"); - } - - /* ============================================================ - * Step 3: Write the final class file. - */ - - /* - * Make sure that constant pool indexes are reserved for the - * following items before starting to write the final class file. - */ - cp.getClass(dotToSlash(className)); - cp.getClass(superclassName); - for (Class intf: interfaces) { - cp.getClass(dotToSlash(intf.getName())); - } - - /* - * Disallow new constant pool additions beyond this point, since - * we are about to write the final constant pool table. - */ - cp.setReadOnly(); - - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(bout); - - try { - /* - * Write all the items of the "ClassFile" structure. - * See JVMS section 4.1. - */ - // u4 magic; - dout.writeInt(0xCAFEBABE); - // u2 minor_version; - dout.writeShort(CLASSFILE_MINOR_VERSION); - // u2 major_version; - dout.writeShort(CLASSFILE_MAJOR_VERSION); - - cp.write(dout); // (write constant pool) - - // u2 access_flags; - dout.writeShort(accessFlags); - // u2 this_class; - dout.writeShort(cp.getClass(dotToSlash(className))); - // u2 super_class; - dout.writeShort(cp.getClass(superclassName)); - - // u2 interfaces_count; - dout.writeShort(interfaces.length); - // u2 interfaces[interfaces_count]; - for (Class intf : interfaces) { - dout.writeShort(cp.getClass( - dotToSlash(intf.getName()))); - } - - // u2 fields_count; - dout.writeShort(fields.size()); - // field_info fields[fields_count]; - for (FieldInfo f : fields) { - f.write(dout); - } - - // u2 methods_count; - dout.writeShort(methods.size()); - // method_info methods[methods_count]; - for (MethodInfo m : methods) { - m.write(dout); - } - - // u2 attributes_count; - dout.writeShort(0); // (no ClassFile attributes for proxy classes) + static byte[] generateProxyClass(ClassLoader loader, + final String name, + List> interfaces, + int accessFlags) { + ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); + final byte[] classFile = gen.generateClassFile(); - } catch (IOException e) { - throw new InternalError("unexpected I/O Exception", e); + if (saveGeneratedFiles) { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + try { + int i = name.lastIndexOf('.'); + Path path; + if (i > 0) { + Path dir = Path.of(dotToSlash(name.substring(0, i))); + Files.createDirectories(dir); + path = dir.resolve(name.substring(i + 1) + ".class"); + } else { + path = Path.of(name + ".class"); + } + Files.write(path, classFile); + return null; + } catch (IOException e) { + throw new InternalError( + "I/O exception saving generated file: " + e); + } + } + }); } - return bout.toByteArray(); + return classFile; } /** - * Add another method to be proxied, either by creating a new - * ProxyMethod object or augmenting an old one for a duplicate - * method. + * Return an array of the type names from an array of Classes. * - * "fromClass" indicates the proxy interface that the method was - * found through, which may be different from (a subinterface of) - * the method's "declaring class". Note that the first Method - * object passed for a given name and descriptor identifies the - * Method object (and thus the declaring class) that will be - * passed to the invocation handler's "invoke" method for a given - * set of duplicate methods. + * @param classes an array of classes or interfaces + * @return the array of class names; or null if there are no classes */ - private void addProxyMethod(Method m, Class fromClass) { - String name = m.getName(); - Class[] parameterTypes = m.getParameterTypes(); - Class returnType = m.getReturnType(); - Class[] exceptionTypes = m.getExceptionTypes(); - - String sig = name + getParameterDescriptors(parameterTypes); - List sigmethods = proxyMethods.get(sig); - if (sigmethods != null) { - for (ProxyMethod pm : sigmethods) { - if (returnType == pm.returnType) { - /* - * Found a match: reduce exception types to the - * greatest set of exceptions that can thrown - * compatibly with the throws clauses of both - * overridden methods. - */ - List> legalExceptions = new ArrayList<>(); - collectCompatibleTypes( - exceptionTypes, pm.exceptionTypes, legalExceptions); - collectCompatibleTypes( - pm.exceptionTypes, exceptionTypes, legalExceptions); - pm.exceptionTypes = new Class[legalExceptions.size()]; - pm.exceptionTypes = - legalExceptions.toArray(pm.exceptionTypes); - return; - } - } - } else { - sigmethods = new ArrayList<>(3); - proxyMethods.put(sig, sigmethods); - } - sigmethods.add(new ProxyMethod(name, parameterTypes, returnType, - exceptionTypes, fromClass)); + 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; } /** @@ -645,16 +237,15 @@ */ LinkedList> uncoveredReturnTypes = new LinkedList<>(); - nextNewReturnType: + nextNewReturnType: for (ProxyMethod pm : methods) { Class newReturnType = pm.returnType; if (newReturnType.isPrimitive()) { throw new IllegalArgumentException( - "methods with same signature " + - getFriendlyMethodSignature(pm.methodName, - pm.parameterTypes) + - " but incompatible return types: " + - newReturnType.getName() + " and others"); + "methods with same signature " + + pm.shortSignature + + " but incompatible return types: " + + newReturnType.getName() + " and others"); } boolean added = false; @@ -709,160 +300,329 @@ if (uncoveredReturnTypes.size() > 1) { ProxyMethod pm = methods.get(0); throw new IllegalArgumentException( - "methods with same signature " + - getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) + - " but incompatible return types: " + uncoveredReturnTypes); + "methods with same signature " + + pm.shortSignature + + " but incompatible return types: " + uncoveredReturnTypes); } } /** - * A FieldInfo object contains information about a particular field - * in the class being generated. The class mirrors the data items of - * the "field_info" structure of the class file format (see JVMS 4.5). - */ - private class FieldInfo { - public int accessFlags; - public String name; - public String descriptor; - - public FieldInfo(String name, String descriptor, int accessFlags) { - this.name = name; - this.descriptor = descriptor; - this.accessFlags = accessFlags; + * Given the exceptions declared in the throws clause of a proxy method, + * compute the exceptions that need to be caught from the invocation + * handler's invoke method and rethrown intact in the method's + * implementation before catching other Throwables and wrapping them + * in UndeclaredThrowableExceptions. + * + * The exceptions to be caught are returned in a List object. Each + * exception in the returned list is guaranteed to not be a subclass of + * any of the other exceptions in the list, so the catch blocks for + * these exceptions may be generated in any order relative to each other. + * + * Error and RuntimeException are each always contained by the returned + * list (if none of their superclasses are contained), since those + * unchecked exceptions should always be rethrown intact, and thus their + * subclasses will never appear in the returned list. + * + * The returned List will be empty if java.lang.Throwable is in the + * given list of declared exceptions, indicating that no exceptions + * need to be caught. + */ + private static List> computeUniqueCatchList(Class[] exceptions) { + List> uniqueList = new ArrayList<>(); + // unique exceptions to catch + uniqueList.add(Error.class); // always catch/rethrow these + uniqueList.add(RuntimeException.class); + + nextException: + for (Class ex : exceptions) { + if (ex.isAssignableFrom(Throwable.class)) { + /* + * If Throwable is declared to be thrown by the proxy method, + * then no catch blocks are necessary, because the invoke + * can, at most, throw Throwable anyway. + */ + uniqueList.clear(); + break; + } else if (!Throwable.class.isAssignableFrom(ex)) { + /* + * Ignore types that cannot be thrown by the invoke method. + */ + continue; + } /* - * Make sure that constant pool indexes are reserved for the - * following items before starting to write the final class file. + * Compare this exception against the current list of + * exceptions that need to be caught: */ - cp.getUtf8(name); - cp.getUtf8(descriptor); + for (int j = 0; j < uniqueList.size(); ) { + Class ex2 = uniqueList.get(j); + if (ex2.isAssignableFrom(ex)) { + /* + * if a superclass of this exception is already on + * the list to catch, then ignore this one and continue; + */ + continue nextException; + } else if (ex.isAssignableFrom(ex2)) { + /* + * if a subclass of this exception is on the list + * to catch, then remove it; + */ + uniqueList.remove(j); + } else { + j++; // else continue comparing. + } + } + // This exception is unique (so far): add it to the list to catch. + uniqueList.add(ex); } + return uniqueList; + } - public void write(DataOutputStream out) throws IOException { - /* - * Write all the items of the "field_info" structure. - * See JVMS section 4.5. - */ - // u2 access_flags; - out.writeShort(accessFlags); - // u2 name_index; - out.writeShort(cp.getUtf8(name)); - // u2 descriptor_index; - out.writeShort(cp.getUtf8(descriptor)); - // u2 attributes_count; - out.writeShort(0); // (no field_info attributes for proxy classes) + /** + * 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 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 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; } } /** - * An ExceptionTableEntry object holds values for the data items of - * an entry in the "exception_table" item of the "Code" attribute of - * "method_info" structures (see JVMS 4.7.3). - */ - private static class ExceptionTableEntry { - public short startPc; - public short endPc; - public short handlerPc; - public short catchType; - - public ExceptionTableEntry(short startPc, short endPc, - short handlerPc, short catchType) - { - this.startPc = startPc; - this.endPc = endPc; - this.handlerPc = handlerPc; - this.catchType = catchType; - } - }; - - /** - * A MethodInfo object contains information about a particular method - * in the class being generated. This class mirrors the data items of - * the "method_info" structure of the class file format (see JVMS 4.6). - */ - private class MethodInfo { - public int accessFlags; - public String name; - public String descriptor; - public short maxStack; - public short maxLocals; - public ByteArrayOutputStream code = new ByteArrayOutputStream(); - public List exceptionTable = - new ArrayList(); - public short[] declaredExceptions; - - public MethodInfo(String name, String descriptor, int accessFlags) { - this.name = name; - this.descriptor = descriptor; - this.accessFlags = accessFlags; + * 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 + * least one of the types in the "with" array. + *

    + * This method is useful for computing the greatest common set of + * declared exceptions from duplicate methods inherited from + * different interfaces. + */ + private static void collectCompatibleTypes(Class[] from, + Class[] with, + List> list) { + for (Class fc : from) { + if (!list.contains(fc)) { + for (Class wc : with) { + if (wc.isAssignableFrom(fc)) { + list.add(fc); + break; + } + } + } + } + } - /* - * Make sure that constant pool indexes are reserved for the - * following items before starting to write the final class file. - */ - cp.getUtf8(name); - cp.getUtf8(descriptor); - cp.getUtf8("Code"); - cp.getUtf8("Exceptions"); + /** + * 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(V14, 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 + * the methods from the proxy interfaces so that the methods from + * java.lang.Object take precedence over duplicate methods in the + * proxy interfaces. + */ + addProxyMethod(hashCodeMethod); + addProxyMethod(equalsMethod); + addProxyMethod(toStringMethod); + + /* + * Accumulate all of the methods from the proxy interfaces. + */ + for (Class intf : interfaces) { + for (Method m : intf.getMethods()) { + if (!Modifier.isStatic(m.getModifiers())) { + addProxyMethod(m, intf); + } + } } - public void write(DataOutputStream out) throws IOException { - /* - * Write all the items of the "method_info" structure. - * See JVMS section 4.6. - */ - // u2 access_flags; - out.writeShort(accessFlags); - // u2 name_index; - out.writeShort(cp.getUtf8(name)); - // u2 descriptor_index; - out.writeShort(cp.getUtf8(descriptor)); - // u2 attributes_count; - out.writeShort(2); // (two method_info attributes:) - - // Write "Code" attribute. See JVMS section 4.7.3. - - // u2 attribute_name_index; - out.writeShort(cp.getUtf8("Code")); - // u4 attribute_length; - out.writeInt(12 + code.size() + 8 * exceptionTable.size()); - // u2 max_stack; - out.writeShort(maxStack); - // u2 max_locals; - out.writeShort(maxLocals); - // u2 code_length; - out.writeInt(code.size()); - // u1 code[code_length]; - code.writeTo(out); - // u2 exception_table_length; - out.writeShort(exceptionTable.size()); - for (ExceptionTableEntry e : exceptionTable) { - // u2 start_pc; - out.writeShort(e.startPc); - // u2 end_pc; - out.writeShort(e.endPc); - // u2 handler_pc; - out.writeShort(e.handlerPc); - // u2 catch_type; - out.writeShort(e.catchType); + /* + * For each set of proxy methods with the same signature, + * verify that the methods' return types are compatible. + */ + for (List sigmethods : proxyMethods.values()) { + checkReturnTypes(sigmethods); + } + + generateConstructor(); + + for (List sigmethods : proxyMethods.values()) { + for (ProxyMethod pm : sigmethods) { + // add static field for the Method object + visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName, + LJLR_METHOD, null, null); + + // Generate code for proxy method + pm.generateMethod(this, className); } - // u2 attributes_count; - out.writeShort(0); + } + + generateStaticInitializer(); + + return toByteArray(); + } - // write "Exceptions" attribute. See JVMS section 4.7.4. + /** + * Add another method to be proxied, either by creating a new + * ProxyMethod object or augmenting an old one for a duplicate + * method. + * + * "fromClass" indicates the proxy interface that the method was + * found through, which may be different from (a subinterface of) + * the method's "declaring class". Note that the first Method + * object passed for a given name and descriptor identifies the + * Method object (and thus the declaring class) that will be + * passed to the invocation handler's "invoke" method for a given + * set of duplicate methods. + */ + private void addProxyMethod(Method m, Class fromClass) { + Class returnType = m.getReturnType(); + Class[] exceptionTypes = m.getExceptionTypes(); - // u2 attribute_name_index; - out.writeShort(cp.getUtf8("Exceptions")); - // u4 attributes_length; - out.writeInt(2 + 2 * declaredExceptions.length); - // u2 number_of_exceptions; - out.writeShort(declaredExceptions.length); - // u2 exception_index_table[number_of_exceptions]; - for (short value : declaredExceptions) { - out.writeShort(value); + String sig = m.toShortSignature(); + List sigmethods = proxyMethods.computeIfAbsent(sig, + (f) -> new ArrayList<>(3)); + for (ProxyMethod pm : sigmethods) { + if (returnType == pm.returnType) { + /* + * Found a match: reduce exception types to the + * greatest set of exceptions that can be thrown + * compatibly with the throws clauses of both + * overridden methods. + */ + List> legalExceptions = new ArrayList<>(); + collectCompatibleTypes( + exceptionTypes, pm.exceptionTypes, legalExceptions); + collectCompatibleTypes( + pm.exceptionTypes, exceptionTypes, legalExceptions); + pm.exceptionTypes = legalExceptions.toArray(EMPTY_CLASS_ARRAY); + return; } } + sigmethods.add(new ProxyMethod(m, sig, m.getParameterTypes(), returnType, + exceptionTypes, fromClass, + "m" + proxyMethodCount++)); + } + + /** + * Add an existing ProxyMethod (hashcode, equals, toString). + * + * @param pm an existing ProxyMethod + */ + private void addProxyMethod(ProxyMethod pm) { + String sig = pm.shortSignature; + List sigmethods = proxyMethods.computeIfAbsent(sig, + (f) -> new ArrayList<>(3)); + sigmethods.add(pm); + } + + /** + * 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(); + } + + /** + * 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); + mv.visitLabel(L_startBlock); + for (List sigmethods : proxyMethods.values()) { + for (ProxyMethod pm : sigmethods) { + pm.codeFieldInitialization(mv, className); + } + } + 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(); } /** @@ -870,35 +630,49 @@ * being generated: a method whose implementation will encode and * dispatch invocations to the proxy instance's invocation handler. */ - private class ProxyMethod { + private static class ProxyMethod { - public String methodName; - public Class[] parameterTypes; - public Class returnType; - public Class[] exceptionTypes; - public Class fromClass; - public String methodFieldName; + private final Method method; + private final String shortSignature; + private final Class fromClass; + private final Class[] parameterTypes; + private final Class returnType; + private final String methodFieldName; + private Class[] exceptionTypes; - private ProxyMethod(String methodName, Class[] parameterTypes, + private ProxyMethod(Method method, String sig, Class[] parameterTypes, Class returnType, Class[] exceptionTypes, - Class fromClass) - { - this.methodName = methodName; + Class fromClass, String methodFieldName) { + this.method = method; + this.shortSignature = sig; this.parameterTypes = parameterTypes; this.returnType = returnType; this.exceptionTypes = exceptionTypes; this.fromClass = fromClass; - this.methodFieldName = "m" + proxyMethodCount++; + this.methodFieldName = methodFieldName; } /** - * Return a MethodInfo object for this method, including generating - * the code and exception table entry. + * Create a new specific ProxyMethod with a specific field name + * + * @param method The method for which to create a proxy + * @param methodFieldName the fieldName to generate */ - private MethodInfo generateMethod() throws IOException { - String desc = getMethodDescriptor(parameterTypes, returnType); - MethodInfo minfo = new MethodInfo(methodName, desc, - ACC_PUBLIC | ACC_FINAL); + private ProxyMethod(Method method, String methodFieldName) { + this(method, method.toShortSignature(), + method.getParameterTypes(), method.getReturnType(), + method.getExceptionTypes(), 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(); + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_FINAL, + method.getName(), desc, null, + typeNames(Arrays.asList(exceptionTypes))); int[] parameterSlot = new int[parameterTypes.length]; int nextSlot = 1; @@ -906,757 +680,234 @@ parameterSlot[i] = nextSlot; nextSlot += getWordsPerType(parameterTypes[i]); } - int localSlot0 = nextSlot; - short pc, tryBegin = 0, tryEnd; - DataOutputStream out = new DataOutputStream(minfo.code); + mv.visitCode(); + Label L_startBlock = new Label(); + Label L_endBlock = new Label(); + Label L_RuntimeHandler = new Label(); + Label L_ThrowableHandler = new Label(); - code_aload(0, out); - - out.writeByte(opc_getfield); - out.writeShort(cp.getFieldRef( - superclassName, - handlerFieldName, "Ljava/lang/reflect/InvocationHandler;")); + List> catchList = computeUniqueCatchList(exceptionTypes); + if (catchList.size() > 0) { + for (Class ex : catchList) { + mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler, + dotToSlash(ex.getName())); + } - code_aload(0, out); + mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler, + JL_THROWABLE); + } + mv.visitLabel(L_startBlock); - out.writeByte(opc_getstatic); - out.writeShort(cp.getFieldRef( - dotToSlash(className), - methodFieldName, "Ljava/lang/reflect/Method;")); + 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) { - - code_ipush(parameterTypes.length, out); - - out.writeByte(opc_anewarray); - out.writeShort(cp.getClass("java/lang/Object")); - + // 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++) { - - out.writeByte(opc_dup); - - code_ipush(i, out); - - codeWrapArgument(parameterTypes[i], parameterSlot[i], out); - - out.writeByte(opc_aastore); + mv.visitInsn(DUP); + emitIconstInsn(mv, i); + codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]); + mv.visitInsn(Opcodes.AASTORE); } } else { - - out.writeByte(opc_aconst_null); + mv.visitInsn(Opcodes.ACONST_NULL); } - out.writeByte(opc_invokeinterface); - out.writeShort(cp.getInterfaceMethodRef( - "java/lang/reflect/InvocationHandler", - "invoke", - "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + - "[Ljava/lang/Object;)Ljava/lang/Object;")); - out.writeByte(4); - out.writeByte(0); + 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) { - - out.writeByte(opc_pop); - - out.writeByte(opc_return); - + mv.visitInsn(POP); + mv.visitInsn(RETURN); } else { - - codeUnwrapReturnValue(returnType, out); + codeUnwrapReturnValue(mv, returnType); } - tryEnd = pc = (short) minfo.code.size(); - - List> catchList = computeUniqueCatchList(exceptionTypes); - if (catchList.size() > 0) { + mv.visitLabel(L_endBlock); - for (Class ex : catchList) { - minfo.exceptionTable.add(new ExceptionTableEntry( - tryBegin, tryEnd, pc, - cp.getClass(dotToSlash(ex.getName())))); - } - - out.writeByte(opc_athrow); - - pc = (short) minfo.code.size(); - - minfo.exceptionTable.add(new ExceptionTableEntry( - tryBegin, tryEnd, pc, cp.getClass("java/lang/Throwable"))); - - code_astore(localSlot0, out); - - out.writeByte(opc_new); - out.writeShort(cp.getClass( - "java/lang/reflect/UndeclaredThrowableException")); - - out.writeByte(opc_dup); - - code_aload(localSlot0, out); - - out.writeByte(opc_invokespecial); - - out.writeShort(cp.getMethodRef( - "java/lang/reflect/UndeclaredThrowableException", - "", "(Ljava/lang/Throwable;)V")); - - out.writeByte(opc_athrow); - } - - if (minfo.code.size() > 65535) { - throw new IllegalArgumentException("code size limit exceeded"); - } - - minfo.maxStack = 10; - minfo.maxLocals = (short) (localSlot0 + 1); - minfo.declaredExceptions = new short[exceptionTypes.length]; - for (int i = 0; i < exceptionTypes.length; i++) { - minfo.declaredExceptions[i] = cp.getClass( - dotToSlash(exceptionTypes[i].getName())); - } - - return minfo; + // 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(); } /** * Generate code for wrapping an argument of the given type * whose value can be found at the specified local variable * index, in order for it to be passed (as an Object) to the - * invocation handler's "invoke" method. The code is written - * to the supplied stream. + * invocation handler's "invoke" method. */ - private void codeWrapArgument(Class type, int slot, - DataOutputStream out) - throws IOException - { + private void codeWrapArgument(MethodVisitor mv, Class type, int slot) { if (type.isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); if (type == int.class || - type == boolean.class || - type == byte.class || - type == char.class || - type == short.class) - { - code_iload(slot, out); + type == boolean.class || + type == byte.class || + type == char.class || + type == short.class) { + mv.visitVarInsn(ILOAD, slot); } else if (type == long.class) { - code_lload(slot, out); + mv.visitVarInsn(LLOAD, slot); } else if (type == float.class) { - code_fload(slot, out); + mv.visitVarInsn(FLOAD, slot); } else if (type == double.class) { - code_dload(slot, out); + mv.visitVarInsn(DLOAD, slot); } else { throw new AssertionError(); } - - out.writeByte(opc_invokestatic); - out.writeShort(cp.getMethodRef( - prim.wrapperClassName, - "valueOf", prim.wrapperValueOfDesc)); - + mv.visitMethodInsn(INVOKESTATIC, prim.wrapperClassName, "valueOf", + prim.wrapperValueOfDesc, false); } else { - - code_aload(slot, out); + mv.visitVarInsn(ALOAD, slot); } } /** * Generate code for unwrapping a return value of the given * type from the invocation handler's "invoke" method (as type - * Object) to its correct type. The code is written to the - * supplied stream. + * Object) to its correct type. */ - private void codeUnwrapReturnValue(Class type, DataOutputStream out) - throws IOException - { + private void codeUnwrapReturnValue(MethodVisitor mv, Class type) { if (type.isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); - out.writeByte(opc_checkcast); - out.writeShort(cp.getClass(prim.wrapperClassName)); - - out.writeByte(opc_invokevirtual); - out.writeShort(cp.getMethodRef( - prim.wrapperClassName, - prim.unwrapMethodName, prim.unwrapMethodDesc)); + mv.visitTypeInsn(CHECKCAST, prim.wrapperClassName); + mv.visitMethodInsn(INVOKEVIRTUAL, + prim.wrapperClassName, + prim.unwrapMethodName, prim.unwrapMethodDesc, false); if (type == int.class || - type == boolean.class || - type == byte.class || - type == char.class || - type == short.class) - { - out.writeByte(opc_ireturn); + type == boolean.class || + type == byte.class || + type == char.class || + type == short.class) { + mv.visitInsn(IRETURN); } else if (type == long.class) { - out.writeByte(opc_lreturn); + mv.visitInsn(LRETURN); } else if (type == float.class) { - out.writeByte(opc_freturn); + mv.visitInsn(FRETURN); } else if (type == double.class) { - out.writeByte(opc_dreturn); + mv.visitInsn(DRETURN); } else { throw new AssertionError(); } - } else { - - out.writeByte(opc_checkcast); - out.writeShort(cp.getClass(dotToSlash(type.getName()))); - - out.writeByte(opc_areturn); + mv.visitTypeInsn(CHECKCAST, dotToSlash(type.getName())); + mv.visitInsn(ARETURN); } } /** * Generate code for initializing the static field that stores - * the Method object for this proxy method. The code is written - * to the supplied stream. + * the Method object for this proxy method. */ - private void codeFieldInitialization(DataOutputStream out) - throws IOException - { - codeClassForName(fromClass, out); + private void codeFieldInitialization(MethodVisitor mv, String className) { + codeClassForName(mv, fromClass); - code_ldc(cp.getString(methodName), out); + mv.visitLdcInsn(method.getName()); - code_ipush(parameterTypes.length, out); + emitIconstInsn(mv, parameterTypes.length); - out.writeByte(opc_anewarray); - out.writeShort(cp.getClass("java/lang/Class")); + mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS); + // Construct an array with the parameter types mapping primitives to Wrapper types for (int i = 0; i < parameterTypes.length; i++) { - - out.writeByte(opc_dup); - - code_ipush(i, out); + mv.visitInsn(DUP); + emitIconstInsn(mv, i); if (parameterTypes[i].isPrimitive()) { PrimitiveTypeInfo prim = - PrimitiveTypeInfo.get(parameterTypes[i]); - - out.writeByte(opc_getstatic); - out.writeShort(cp.getFieldRef( - prim.wrapperClassName, "TYPE", "Ljava/lang/Class;")); - + PrimitiveTypeInfo.get(parameterTypes[i]); + mv.visitFieldInsn(GETSTATIC, + prim.wrapperClassName, "TYPE", LJL_CLASS); } else { - codeClassForName(parameterTypes[i], out); + codeClassForName(mv, parameterTypes[i]); } - - out.writeByte(opc_aastore); - } - - out.writeByte(opc_invokevirtual); - out.writeShort(cp.getMethodRef( - "java/lang/Class", - "getMethod", - "(Ljava/lang/String;[Ljava/lang/Class;)" + - "Ljava/lang/reflect/Method;")); - - out.writeByte(opc_putstatic); - out.writeShort(cp.getFieldRef( - dotToSlash(className), - methodFieldName, "Ljava/lang/reflect/Method;")); - } - } - - /** - * Generate the constructor method for the proxy class. - */ - private MethodInfo generateConstructor() throws IOException { - MethodInfo minfo = new MethodInfo( - "", "(Ljava/lang/reflect/InvocationHandler;)V", - ACC_PUBLIC); - - DataOutputStream out = new DataOutputStream(minfo.code); - - code_aload(0, out); - - code_aload(1, out); - - out.writeByte(opc_invokespecial); - out.writeShort(cp.getMethodRef( - superclassName, - "", "(Ljava/lang/reflect/InvocationHandler;)V")); - - out.writeByte(opc_return); - - minfo.maxStack = 10; - minfo.maxLocals = 2; - minfo.declaredExceptions = new short[0]; - - return minfo; - } - - /** - * Generate the static initializer method for the proxy class. - */ - private MethodInfo generateStaticInitializer() throws IOException { - MethodInfo minfo = new MethodInfo( - "", "()V", ACC_STATIC); - - int localSlot0 = 1; - short pc, tryBegin = 0, tryEnd; - - DataOutputStream out = new DataOutputStream(minfo.code); - - for (List sigmethods : proxyMethods.values()) { - for (ProxyMethod pm : sigmethods) { - pm.codeFieldInitialization(out); + mv.visitInsn(Opcodes.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); } - out.writeByte(opc_return); - - tryEnd = pc = (short) minfo.code.size(); - - minfo.exceptionTable.add(new ExceptionTableEntry( - tryBegin, tryEnd, pc, - cp.getClass("java/lang/NoSuchMethodException"))); - - code_astore(localSlot0, out); - - out.writeByte(opc_new); - out.writeShort(cp.getClass("java/lang/NoSuchMethodError")); - - out.writeByte(opc_dup); - - code_aload(localSlot0, out); - - out.writeByte(opc_invokevirtual); - out.writeShort(cp.getMethodRef( - "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); - - out.writeByte(opc_invokespecial); - out.writeShort(cp.getMethodRef( - "java/lang/NoSuchMethodError", "", "(Ljava/lang/String;)V")); - - out.writeByte(opc_athrow); - - pc = (short) minfo.code.size(); - - minfo.exceptionTable.add(new ExceptionTableEntry( - tryBegin, tryEnd, pc, - cp.getClass("java/lang/ClassNotFoundException"))); - - code_astore(localSlot0, out); - - out.writeByte(opc_new); - out.writeShort(cp.getClass("java/lang/NoClassDefFoundError")); - - out.writeByte(opc_dup); - - code_aload(localSlot0, out); - - out.writeByte(opc_invokevirtual); - out.writeShort(cp.getMethodRef( - "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); - - out.writeByte(opc_invokespecial); - out.writeShort(cp.getMethodRef( - "java/lang/NoClassDefFoundError", - "", "(Ljava/lang/String;)V")); - - out.writeByte(opc_athrow); - - if (minfo.code.size() > 65535) { - throw new IllegalArgumentException("code size limit exceeded"); - } - - minfo.maxStack = 10; - minfo.maxLocals = (short) (localSlot0 + 1); - minfo.declaredExceptions = new short[0]; - - return minfo; - } - - - /* - * =============== Code Generation Utility Methods =============== - */ - - /* - * The following methods generate code for the load or store operation - * indicated by their name for the given local variable. The code is - * written to the supplied stream. - */ - - private void code_iload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_iload, opc_iload_0, out); - } - - private void code_lload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_lload, opc_lload_0, out); - } - - private void code_fload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_fload, opc_fload_0, out); - } - - private void code_dload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_dload, opc_dload_0, out); - } - - private void code_aload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_aload, opc_aload_0, out); - } - -// private void code_istore(int lvar, DataOutputStream out) -// throws IOException -// { -// codeLocalLoadStore(lvar, opc_istore, opc_istore_0, out); -// } - -// private void code_lstore(int lvar, DataOutputStream out) -// throws IOException -// { -// codeLocalLoadStore(lvar, opc_lstore, opc_lstore_0, out); -// } - -// private void code_fstore(int lvar, DataOutputStream out) -// throws IOException -// { -// codeLocalLoadStore(lvar, opc_fstore, opc_fstore_0, out); -// } - -// private void code_dstore(int lvar, DataOutputStream out) -// throws IOException -// { -// codeLocalLoadStore(lvar, opc_dstore, opc_dstore_0, out); -// } - - private void code_astore(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_astore, opc_astore_0, out); - } - - /** - * Generate code for a load or store instruction for the given local - * variable. The code is written to the supplied stream. - * - * "opcode" indicates the opcode form of the desired load or store - * instruction that takes an explicit local variable index, and - * "opcode_0" indicates the corresponding form of the instruction - * with the implicit index 0. - */ - private void codeLocalLoadStore(int lvar, int opcode, int opcode_0, - DataOutputStream out) - throws IOException - { - assert lvar >= 0 && lvar <= 0xFFFF; - if (lvar <= 3) { - out.writeByte(opcode_0 + lvar); - } else if (lvar <= 0xFF) { - out.writeByte(opcode); - out.writeByte(lvar & 0xFF); - } else { - /* - * Use the "wide" instruction modifier for local variable - * indexes that do not fit into an unsigned byte. - */ - out.writeByte(opc_wide); - out.writeByte(opcode); - out.writeShort(lvar & 0xFFFF); - } - } - - /** - * Generate code for an "ldc" instruction for the given constant pool - * index (the "ldc_w" instruction is used if the index does not fit - * into an unsigned byte). The code is written to the supplied stream. - */ - private void code_ldc(int index, DataOutputStream out) - throws IOException - { - assert index >= 0 && index <= 0xFFFF; - if (index <= 0xFF) { - out.writeByte(opc_ldc); - out.writeByte(index & 0xFF); - } else { - out.writeByte(opc_ldc_w); - out.writeShort(index & 0xFFFF); - } - } - - /** - * Generate code to push a constant integer value on to the operand - * stack, using the "iconst_", "bipush", or "sipush" instructions - * depending on the size of the value. The code is written to the - * supplied stream. - */ - private void code_ipush(int value, DataOutputStream out) - throws IOException - { - if (value >= -1 && value <= 5) { - out.writeByte(opc_iconst_0 + value); - } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { - out.writeByte(opc_bipush); - out.writeByte(value & 0xFF); - } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { - out.writeByte(opc_sipush); - out.writeShort(value & 0xFFFF); - } else { - throw new AssertionError(); - } - } - - /** - * Generate code to invoke the Class.forName with the name of the given - * class to get its Class object at runtime. The code is written to - * the supplied stream. Note that the code generated by this method - * may cause the checked ClassNotFoundException to be thrown. - */ - private void codeClassForName(Class cl, DataOutputStream out) - throws IOException - { - code_ldc(cp.getString(cl.getName()), out); - - out.writeByte(opc_invokestatic); - out.writeShort(cp.getMethodRef( - "java/lang/Class", - "forName", "(Ljava/lang/String;)Ljava/lang/Class;")); - } - - - /* - * ==================== General Utility Methods ==================== - */ - - /** - * 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 4.2). - */ - private static String dotToSlash(String name) { - return name.replace('.', '/'); - } - - /** - * Return the "method descriptor" string for a method with the given - * parameter types and return type. See JVMS section 4.3.3. - */ - private static String getMethodDescriptor(Class[] parameterTypes, - Class returnType) - { - return getParameterDescriptors(parameterTypes) + - ((returnType == void.class) ? "V" : getFieldType(returnType)); - } - - /** - * Return the list of "parameter descriptor" strings enclosed in - * parentheses corresponding to the given parameter types (in other - * words, a method descriptor without a return descriptor). This - * string is useful for constructing string keys for methods without - * regard to their return type. - */ - private static String getParameterDescriptors(Class[] parameterTypes) { - StringBuilder desc = new StringBuilder("("); - for (int i = 0; i < parameterTypes.length; i++) { - desc.append(getFieldType(parameterTypes[i])); - } - desc.append(')'); - return desc.toString(); - } + /* + * =============== Code Generation Utility Methods =============== + */ - /** - * Return the "field type" string for the given type, appropriate for - * a field descriptor, a parameter descriptor, or a return descriptor - * other than "void". See JVMS section 4.3.2. - */ - private static String getFieldType(Class type) { - if (type.isPrimitive()) { - return PrimitiveTypeInfo.get(type).baseTypeString; - } else if (type.isArray()) { - /* - * According to JLS 20.3.2, the getName() method on Class does - * return the VM type descriptor format for array classes (only); - * using that should be quicker than the otherwise obvious code: - * - * return "[" + getTypeDescriptor(type.getComponentType()); - */ - return type.getName().replace('.', '/'); - } else { - return "L" + dotToSlash(type.getName()) + ";"; + /** + * Generate code to invoke the Class.forName with the name of the given + * class to get its Class object at runtime. The code is written to + * the supplied stream. Note that the code generated by this method + * may cause the checked ClassNotFoundException to be thrown. + */ + private void codeClassForName(MethodVisitor mv, Class cl) { + mv.visitLdcInsn(cl.getName()); + mv.visitMethodInsn(INVOKESTATIC, + JL_CLASS, + "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false); } - } - /** - * Returns a human-readable string representing the signature of a - * method with the given name and parameter types. - */ - private static String getFriendlyMethodSignature(String name, - Class[] parameterTypes) - { - StringBuilder sig = new StringBuilder(name); - sig.append('('); - for (int i = 0; i < parameterTypes.length; i++) { - if (i > 0) { - sig.append(','); - } - Class parameterType = parameterTypes[i]; - int dimensions = 0; - while (parameterType.isArray()) { - parameterType = parameterType.getComponentType(); - dimensions++; - } - sig.append(parameterType.getName()); - while (dimensions-- > 0) { - sig.append("[]"); + /** + * 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); } } - sig.append(')'); - return sig.toString(); - } - /** - * Return the number of abstract "words", or consecutive local variable - * indexes, required to contain a value of the given type. See JVMS - * section 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 - * least one of the types in the "with" array. - * - * This method is useful for computing the greatest common set of - * declared exceptions from duplicate methods inherited from - * different interfaces. - */ - private static void collectCompatibleTypes(Class[] from, - Class[] with, - List> list) - { - for (Class fc: from) { - if (!list.contains(fc)) { - for (Class wc: with) { - if (wc.isAssignableFrom(fc)) { - list.add(fc); - break; - } - } - } + @Override + public String toString() { + return method.toShortString(); } } /** - * Given the exceptions declared in the throws clause of a proxy method, - * compute the exceptions that need to be caught from the invocation - * handler's invoke method and rethrown intact in the method's - * implementation before catching other Throwables and wrapping them - * in UndeclaredThrowableExceptions. - * - * The exceptions to be caught are returned in a List object. Each - * exception in the returned list is guaranteed to not be a subclass of - * any of the other exceptions in the list, so the catch blocks for - * these exceptions may be generated in any order relative to each other. - * - * Error and RuntimeException are each always contained by the returned - * list (if none of their superclasses are contained), since those - * unchecked exceptions should always be rethrown intact, and thus their - * subclasses will never appear in the returned list. - * - * The returned List will be empty if java.lang.Throwable is in the - * given list of declared exceptions, indicating that no exceptions - * need to be caught. - */ - private static List> computeUniqueCatchList(Class[] exceptions) { - List> uniqueList = new ArrayList<>(); - // unique exceptions to catch - - uniqueList.add(Error.class); // always catch/rethrow these - uniqueList.add(RuntimeException.class); - - nextException: - for (Class ex: exceptions) { - if (ex.isAssignableFrom(Throwable.class)) { - /* - * If Throwable is declared to be thrown by the proxy method, - * then no catch blocks are necessary, because the invoke - * can, at most, throw Throwable anyway. - */ - uniqueList.clear(); - break; - } else if (!Throwable.class.isAssignableFrom(ex)) { - /* - * Ignore types that cannot be thrown by the invoke method. - */ - continue; - } - /* - * Compare this exception against the current list of - * exceptions that need to be caught: - */ - for (int j = 0; j < uniqueList.size();) { - Class ex2 = uniqueList.get(j); - if (ex2.isAssignableFrom(ex)) { - /* - * if a superclass of this exception is already on - * the list to catch, then ignore this one and continue; - */ - continue nextException; - } else if (ex.isAssignableFrom(ex2)) { - /* - * if a subclass of this exception is on the list - * to catch, then remove it; - */ - uniqueList.remove(j); - } else { - j++; // else continue comparing. - } - } - // This exception is unique (so far): add it to the list to catch. - uniqueList.add(ex); - } - return uniqueList; - } - - /** * A PrimitiveTypeInfo object contains assorted information about * a primitive type in its public fields. The struct for a particular * primitive type can be obtained using the static "get" method. */ private static class PrimitiveTypeInfo { - /** "base type" used in various descriptors (see JVMS section 4.3.2) */ - public String baseTypeString; - - /** name of corresponding wrapper class */ - public String wrapperClassName; - - /** method descriptor for wrapper class "valueOf" factory method */ - public String wrapperValueOfDesc; - - /** name of wrapper class method for retrieving primitive value */ - public String unwrapMethodName; + private static Map, PrimitiveTypeInfo> table = new HashMap<>(); - /** descriptor of same method */ - public String unwrapMethodDesc; - - private static Map,PrimitiveTypeInfo> table = new HashMap<>(); static { add(byte.class, Byte.class); add(char.class, Character.class); @@ -1668,365 +919,46 @@ add(boolean.class, Boolean.class); } - private static void add(Class primitiveClass, Class wrapperClass) { - table.put(primitiveClass, - new PrimitiveTypeInfo(primitiveClass, wrapperClass)); - } - - private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) { - assert primitiveClass.isPrimitive(); - - baseTypeString = - Array.newInstance(primitiveClass, 0) - .getClass().getName().substring(1); - wrapperClassName = dotToSlash(wrapperClass.getName()); - wrapperValueOfDesc = - "(" + baseTypeString + ")L" + wrapperClassName + ";"; - unwrapMethodName = primitiveClass.getName() + "Value"; - unwrapMethodDesc = "()" + baseTypeString; - } - - public static PrimitiveTypeInfo get(Class cl) { - return table.get(cl); - } - } - - - /** - * A ConstantPool object represents the constant pool of a class file - * being generated. This representation of a constant pool is designed - * specifically for use by ProxyGenerator; in particular, it assumes - * that constant pool entries will not need to be resorted (for example, - * by their type, as the Java compiler does), so that the final index - * value can be assigned and used when an entry is first created. - * - * Note that new entries cannot be created after the constant pool has - * been written to a class file. To prevent such logic errors, a - * ConstantPool instance can be marked "read only", so that further - * attempts to add new entries will fail with a runtime exception. - * - * See JVMS section 4.4 for more information about the constant pool - * of a class file. - */ - private static class ConstantPool { - - /** - * list of constant pool entries, in constant pool index order. - * - * This list is used when writing the constant pool to a stream - * and for assigning the next index value. Note that element 0 - * of this list corresponds to constant pool index 1. - */ - private List pool = new ArrayList<>(32); - - /** - * maps constant pool data of all types to constant pool indexes. - * - * This map is used to look up the index of an existing entry for - * values of all types. - */ - private Map map = new HashMap<>(16); - - /** true if no new constant pool entries may be added */ - private boolean readOnly = false; - - /** - * Get or assign the index for a CONSTANT_Utf8 entry. - */ - public short getUtf8(String s) { - if (s == null) { - throw new NullPointerException(); - } - return getValue(s); - } - - /** - * Get or assign the index for a CONSTANT_Integer entry. - */ - public short getInteger(int i) { - return getValue(i); - } - - /** - * Get or assign the index for a CONSTANT_Float entry. - */ - public short getFloat(float f) { - return getValue(f); - } - /** - * Get or assign the index for a CONSTANT_Class entry. + * name of corresponding wrapper class */ - public short getClass(String name) { - short utf8Index = getUtf8(name); - return getIndirect(new IndirectEntry( - CONSTANT_CLASS, utf8Index)); - } - - /** - * Get or assign the index for a CONSTANT_String entry. - */ - public short getString(String s) { - short utf8Index = getUtf8(s); - return getIndirect(new IndirectEntry( - CONSTANT_STRING, utf8Index)); - } - - /** - * Get or assign the index for a CONSTANT_FieldRef entry. - */ - public short getFieldRef(String className, - String name, String descriptor) - { - short classIndex = getClass(className); - short nameAndTypeIndex = getNameAndType(name, descriptor); - return getIndirect(new IndirectEntry( - CONSTANT_FIELD, classIndex, nameAndTypeIndex)); - } - - /** - * Get or assign the index for a CONSTANT_MethodRef entry. - */ - public short getMethodRef(String className, - String name, String descriptor) - { - short classIndex = getClass(className); - short nameAndTypeIndex = getNameAndType(name, descriptor); - return getIndirect(new IndirectEntry( - CONSTANT_METHOD, classIndex, nameAndTypeIndex)); - } - - /** - * Get or assign the index for a CONSTANT_InterfaceMethodRef entry. - */ - public short getInterfaceMethodRef(String className, String name, - String descriptor) - { - short classIndex = getClass(className); - short nameAndTypeIndex = getNameAndType(name, descriptor); - return getIndirect(new IndirectEntry( - CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex)); - } - + private String wrapperClassName; /** - * Get or assign the index for a CONSTANT_NameAndType entry. + * method descriptor for wrapper class "valueOf" factory method */ - public short getNameAndType(String name, String descriptor) { - short nameIndex = getUtf8(name); - short descriptorIndex = getUtf8(descriptor); - return getIndirect(new IndirectEntry( - CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex)); - } - + private String wrapperValueOfDesc; /** - * Set this ConstantPool instance to be "read only". - * - * After this method has been called, further requests to get - * an index for a non-existent entry will cause an InternalError - * to be thrown instead of creating of the entry. + * name of wrapper class method for retrieving primitive value */ - public void setReadOnly() { - readOnly = true; - } - + private String unwrapMethodName; /** - * Write this constant pool to a stream as part of - * the class file format. - * - * This consists of writing the "constant_pool_count" and - * "constant_pool[]" items of the "ClassFile" structure, as - * described in JVMS section 4.1. + * descriptor of same method */ - public void write(OutputStream out) throws IOException { - DataOutputStream dataOut = new DataOutputStream(out); + private String unwrapMethodDesc; - // constant_pool_count: number of entries plus one - dataOut.writeShort(pool.size() + 1); - - for (Entry e : pool) { - e.write(dataOut); - } - } + private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) { + assert primitiveClass.isPrimitive(); - /** - * Add a new constant pool entry and return its index. - */ - private short addEntry(Entry entry) { - pool.add(entry); - /* - * Note that this way of determining the index of the - * added entry is wrong if this pool supports - * CONSTANT_Long or CONSTANT_Double entries. + /** + * "base type" used in various descriptors (see JVMS section 4.3.2) */ - if (pool.size() >= 65535) { - throw new IllegalArgumentException( - "constant pool size limit exceeded"); - } - return (short) pool.size(); - } - - /** - * Get or assign the index for an entry of a type that contains - * a direct value. The type of the given object determines the - * type of the desired entry as follows: - * - * java.lang.String CONSTANT_Utf8 - * java.lang.Integer CONSTANT_Integer - * java.lang.Float CONSTANT_Float - * java.lang.Long CONSTANT_Long - * java.lang.Double CONSTANT_DOUBLE - */ - private short getValue(Object key) { - Integer index = map.get(key); - if (index != null) { - return index.shortValue(); - } else { - if (readOnly) { - throw new InternalError( - "late constant pool addition: " + key); - } - short i = addEntry(new ValueEntry(key)); - map.put(key, (int)i); - return i; - } - } - - /** - * Get or assign the index for an entry of a type that contains - * references to other constant pool entries. - */ - private short getIndirect(IndirectEntry e) { - Integer index = map.get(e); - if (index != null) { - return index.shortValue(); - } else { - if (readOnly) { - throw new InternalError("late constant pool addition"); - } - short i = addEntry(e); - map.put(e, (int)i); - return i; - } - } - - /** - * Entry is the abstact superclass of all constant pool entry types - * that can be stored in the "pool" list; its purpose is to define a - * common method for writing constant pool entries to a class file. - */ - private abstract static class Entry { - public abstract void write(DataOutputStream out) - throws IOException; + String baseTypeString = + Array.newInstance(primitiveClass, 0) + .getClass().getName().substring(1); + wrapperClassName = dotToSlash(wrapperClass.getName()); + wrapperValueOfDesc = + "(" + baseTypeString + ")L" + wrapperClassName + ";"; + unwrapMethodName = primitiveClass.getName() + "Value"; + unwrapMethodDesc = "()" + baseTypeString; } - /** - * ValueEntry represents a constant pool entry of a type that - * contains a direct value (see the comments for the "getValue" - * method for a list of such types). - * - * ValueEntry objects are not used as keys for their entries in the - * Map "map", so no useful hashCode or equals methods are defined. - */ - private static class ValueEntry extends Entry { - private Object value; - - public ValueEntry(Object value) { - this.value = value; - } - - public void write(DataOutputStream out) throws IOException { - if (value instanceof String) { - out.writeByte(CONSTANT_UTF8); - out.writeUTF((String) value); - } else if (value instanceof Integer) { - out.writeByte(CONSTANT_INTEGER); - out.writeInt(((Integer) value).intValue()); - } else if (value instanceof Float) { - out.writeByte(CONSTANT_FLOAT); - out.writeFloat(((Float) value).floatValue()); - } else if (value instanceof Long) { - out.writeByte(CONSTANT_LONG); - out.writeLong(((Long) value).longValue()); - } else if (value instanceof Double) { - out.writeDouble(CONSTANT_DOUBLE); - out.writeDouble(((Double) value).doubleValue()); - } else { - throw new InternalError("bogus value entry: " + value); - } - } + private static void add(Class primitiveClass, Class wrapperClass) { + table.put(primitiveClass, + new PrimitiveTypeInfo(primitiveClass, wrapperClass)); } - /** - * IndirectEntry represents a constant pool entry of a type that - * references other constant pool entries, i.e., the following types: - * - * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref, - * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and - * CONSTANT_NameAndType. - * - * Each of these entry types contains either one or two indexes of - * other constant pool entries. - * - * IndirectEntry objects are used as the keys for their entries in - * the Map "map", so the hashCode and equals methods are overridden - * to allow matching. - */ - private static class IndirectEntry extends Entry { - private int tag; - private short index0; - private short index1; - - /** - * Construct an IndirectEntry for a constant pool entry type - * that contains one index of another entry. - */ - public IndirectEntry(int tag, short index) { - this.tag = tag; - this.index0 = index; - this.index1 = 0; - } - - /** - * Construct an IndirectEntry for a constant pool entry type - * that contains two indexes for other entries. - */ - public IndirectEntry(int tag, short index0, short index1) { - this.tag = tag; - this.index0 = index0; - this.index1 = index1; - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeShort(index0); - /* - * If this entry type contains two indexes, write - * out the second, too. - */ - if (tag == CONSTANT_FIELD || - tag == CONSTANT_METHOD || - tag == CONSTANT_INTERFACEMETHOD || - tag == CONSTANT_NAMEANDTYPE) - { - out.writeShort(index1); - } - } - - public int hashCode() { - return tag + index0 + index1; - } - - public boolean equals(Object obj) { - if (obj instanceof IndirectEntry) { - IndirectEntry other = (IndirectEntry) obj; - if (tag == other.tag && - index0 == other.index0 && index1 == other.index1) - { - return true; - } - } - return false; - } + public static PrimitiveTypeInfo get(Class cl) { + return table.get(cl); } } } --- old/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java 2019-08-21 16:28:43.348032920 -0400 +++ /dev/null 2019-08-16 15:28:57.192077812 -0400 @@ -1,2032 +0,0 @@ -/* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.lang.reflect; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import sun.security.action.GetBooleanAction; - -/** - * ProxyGenerator contains the code to generate a dynamic proxy class - * for the java.lang.reflect.Proxy API. - * - * The external interfaces to ProxyGenerator is the static - * "generateProxyClass" method. - * - * @author Peter Jones - * @since 1.3 - */ -class ProxyGenerator { - /* - * In the comments below, "JVMS" refers to The Java Virtual Machine - * Specification Second Edition and "JLS" refers to the original - * version of The Java Language Specification, unless otherwise - * specified. - */ - - /* generate 1.5-era class file version */ - private static final int CLASSFILE_MAJOR_VERSION = 49; - private static final int CLASSFILE_MINOR_VERSION = 0; - - /* - * beginning of constants copied from - * sun.tools.java.RuntimeConstants (which no longer exists): - */ - - /* constant pool tags */ - private static final int CONSTANT_UTF8 = 1; - private static final int CONSTANT_UNICODE = 2; - private static final int CONSTANT_INTEGER = 3; - private static final int CONSTANT_FLOAT = 4; - private static final int CONSTANT_LONG = 5; - private static final int CONSTANT_DOUBLE = 6; - private static final int CONSTANT_CLASS = 7; - private static final int CONSTANT_STRING = 8; - private static final int CONSTANT_FIELD = 9; - private static final int CONSTANT_METHOD = 10; - private static final int CONSTANT_INTERFACEMETHOD = 11; - private static final int CONSTANT_NAMEANDTYPE = 12; - - /* access and modifier flags */ - private static final int ACC_PUBLIC = 0x00000001; - private static final int ACC_PRIVATE = 0x00000002; -// private static final int ACC_PROTECTED = 0x00000004; - private static final int ACC_STATIC = 0x00000008; - private static final int ACC_FINAL = 0x00000010; -// private static final int ACC_SYNCHRONIZED = 0x00000020; -// private static final int ACC_VOLATILE = 0x00000040; -// private static final int ACC_TRANSIENT = 0x00000080; -// private static final int ACC_NATIVE = 0x00000100; -// private static final int ACC_INTERFACE = 0x00000200; -// private static final int ACC_ABSTRACT = 0x00000400; - private static final int ACC_SUPER = 0x00000020; -// private static final int ACC_STRICT = 0x00000800; - - /* opcodes */ -// private static final int opc_nop = 0; - private static final int opc_aconst_null = 1; -// private static final int opc_iconst_m1 = 2; - private static final int opc_iconst_0 = 3; -// private static final int opc_iconst_1 = 4; -// private static final int opc_iconst_2 = 5; -// private static final int opc_iconst_3 = 6; -// private static final int opc_iconst_4 = 7; -// private static final int opc_iconst_5 = 8; -// private static final int opc_lconst_0 = 9; -// private static final int opc_lconst_1 = 10; -// private static final int opc_fconst_0 = 11; -// private static final int opc_fconst_1 = 12; -// private static final int opc_fconst_2 = 13; -// private static final int opc_dconst_0 = 14; -// private static final int opc_dconst_1 = 15; - private static final int opc_bipush = 16; - private static final int opc_sipush = 17; - private static final int opc_ldc = 18; - private static final int opc_ldc_w = 19; -// private static final int opc_ldc2_w = 20; - private static final int opc_iload = 21; - private static final int opc_lload = 22; - private static final int opc_fload = 23; - private static final int opc_dload = 24; - private static final int opc_aload = 25; - private static final int opc_iload_0 = 26; -// private static final int opc_iload_1 = 27; -// private static final int opc_iload_2 = 28; -// private static final int opc_iload_3 = 29; - private static final int opc_lload_0 = 30; -// private static final int opc_lload_1 = 31; -// private static final int opc_lload_2 = 32; -// private static final int opc_lload_3 = 33; - private static final int opc_fload_0 = 34; -// private static final int opc_fload_1 = 35; -// private static final int opc_fload_2 = 36; -// private static final int opc_fload_3 = 37; - private static final int opc_dload_0 = 38; -// private static final int opc_dload_1 = 39; -// private static final int opc_dload_2 = 40; -// private static final int opc_dload_3 = 41; - private static final int opc_aload_0 = 42; -// private static final int opc_aload_1 = 43; -// private static final int opc_aload_2 = 44; -// private static final int opc_aload_3 = 45; -// private static final int opc_iaload = 46; -// private static final int opc_laload = 47; -// private static final int opc_faload = 48; -// private static final int opc_daload = 49; -// private static final int opc_aaload = 50; -// private static final int opc_baload = 51; -// private static final int opc_caload = 52; -// private static final int opc_saload = 53; -// private static final int opc_istore = 54; -// private static final int opc_lstore = 55; -// private static final int opc_fstore = 56; -// private static final int opc_dstore = 57; - private static final int opc_astore = 58; -// private static final int opc_istore_0 = 59; -// private static final int opc_istore_1 = 60; -// private static final int opc_istore_2 = 61; -// private static final int opc_istore_3 = 62; -// private static final int opc_lstore_0 = 63; -// private static final int opc_lstore_1 = 64; -// private static final int opc_lstore_2 = 65; -// private static final int opc_lstore_3 = 66; -// private static final int opc_fstore_0 = 67; -// private static final int opc_fstore_1 = 68; -// private static final int opc_fstore_2 = 69; -// private static final int opc_fstore_3 = 70; -// private static final int opc_dstore_0 = 71; -// private static final int opc_dstore_1 = 72; -// private static final int opc_dstore_2 = 73; -// private static final int opc_dstore_3 = 74; - private static final int opc_astore_0 = 75; -// private static final int opc_astore_1 = 76; -// private static final int opc_astore_2 = 77; -// private static final int opc_astore_3 = 78; -// private static final int opc_iastore = 79; -// private static final int opc_lastore = 80; -// private static final int opc_fastore = 81; -// private static final int opc_dastore = 82; - private static final int opc_aastore = 83; -// private static final int opc_bastore = 84; -// private static final int opc_castore = 85; -// private static final int opc_sastore = 86; - private static final int opc_pop = 87; -// private static final int opc_pop2 = 88; - private static final int opc_dup = 89; -// private static final int opc_dup_x1 = 90; -// private static final int opc_dup_x2 = 91; -// private static final int opc_dup2 = 92; -// private static final int opc_dup2_x1 = 93; -// private static final int opc_dup2_x2 = 94; -// private static final int opc_swap = 95; -// private static final int opc_iadd = 96; -// private static final int opc_ladd = 97; -// private static final int opc_fadd = 98; -// private static final int opc_dadd = 99; -// private static final int opc_isub = 100; -// private static final int opc_lsub = 101; -// private static final int opc_fsub = 102; -// private static final int opc_dsub = 103; -// private static final int opc_imul = 104; -// private static final int opc_lmul = 105; -// private static final int opc_fmul = 106; -// private static final int opc_dmul = 107; -// private static final int opc_idiv = 108; -// private static final int opc_ldiv = 109; -// private static final int opc_fdiv = 110; -// private static final int opc_ddiv = 111; -// private static final int opc_irem = 112; -// private static final int opc_lrem = 113; -// private static final int opc_frem = 114; -// private static final int opc_drem = 115; -// private static final int opc_ineg = 116; -// private static final int opc_lneg = 117; -// private static final int opc_fneg = 118; -// private static final int opc_dneg = 119; -// private static final int opc_ishl = 120; -// private static final int opc_lshl = 121; -// private static final int opc_ishr = 122; -// private static final int opc_lshr = 123; -// private static final int opc_iushr = 124; -// private static final int opc_lushr = 125; -// private static final int opc_iand = 126; -// private static final int opc_land = 127; -// private static final int opc_ior = 128; -// private static final int opc_lor = 129; -// private static final int opc_ixor = 130; -// private static final int opc_lxor = 131; -// private static final int opc_iinc = 132; -// private static final int opc_i2l = 133; -// private static final int opc_i2f = 134; -// private static final int opc_i2d = 135; -// private static final int opc_l2i = 136; -// private static final int opc_l2f = 137; -// private static final int opc_l2d = 138; -// private static final int opc_f2i = 139; -// private static final int opc_f2l = 140; -// private static final int opc_f2d = 141; -// private static final int opc_d2i = 142; -// private static final int opc_d2l = 143; -// private static final int opc_d2f = 144; -// private static final int opc_i2b = 145; -// private static final int opc_i2c = 146; -// private static final int opc_i2s = 147; -// private static final int opc_lcmp = 148; -// private static final int opc_fcmpl = 149; -// private static final int opc_fcmpg = 150; -// private static final int opc_dcmpl = 151; -// private static final int opc_dcmpg = 152; -// private static final int opc_ifeq = 153; -// private static final int opc_ifne = 154; -// private static final int opc_iflt = 155; -// private static final int opc_ifge = 156; -// private static final int opc_ifgt = 157; -// private static final int opc_ifle = 158; -// private static final int opc_if_icmpeq = 159; -// private static final int opc_if_icmpne = 160; -// private static final int opc_if_icmplt = 161; -// private static final int opc_if_icmpge = 162; -// private static final int opc_if_icmpgt = 163; -// private static final int opc_if_icmple = 164; -// private static final int opc_if_acmpeq = 165; -// private static final int opc_if_acmpne = 166; -// private static final int opc_goto = 167; -// private static final int opc_jsr = 168; -// private static final int opc_ret = 169; -// private static final int opc_tableswitch = 170; -// private static final int opc_lookupswitch = 171; - private static final int opc_ireturn = 172; - private static final int opc_lreturn = 173; - private static final int opc_freturn = 174; - private static final int opc_dreturn = 175; - private static final int opc_areturn = 176; - private static final int opc_return = 177; - private static final int opc_getstatic = 178; - private static final int opc_putstatic = 179; - private static final int opc_getfield = 180; -// private static final int opc_putfield = 181; - private static final int opc_invokevirtual = 182; - private static final int opc_invokespecial = 183; - private static final int opc_invokestatic = 184; - private static final int opc_invokeinterface = 185; - private static final int opc_new = 187; -// private static final int opc_newarray = 188; - private static final int opc_anewarray = 189; -// private static final int opc_arraylength = 190; - private static final int opc_athrow = 191; - private static final int opc_checkcast = 192; -// private static final int opc_instanceof = 193; -// private static final int opc_monitorenter = 194; -// private static final int opc_monitorexit = 195; - private static final int opc_wide = 196; -// private static final int opc_multianewarray = 197; -// private static final int opc_ifnull = 198; -// private static final int opc_ifnonnull = 199; -// private static final int opc_goto_w = 200; -// private static final int opc_jsr_w = 201; - - // end of constants copied from sun.tools.java.RuntimeConstants - - /** name of the superclass of proxy classes */ - private static final String superclassName = "java/lang/reflect/Proxy"; - - /** name of field for storing a proxy instance's invocation handler */ - private static final String handlerFieldName = "h"; - - /** debugging flag for saving generated class files */ - private static final boolean saveGeneratedFiles = - java.security.AccessController.doPrivileged( - new GetBooleanAction( - "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue(); - - /** - * Generate a public proxy class given a name and a list of proxy interfaces. - */ - static byte[] generateProxyClass(final String name, - Class[] interfaces) { - return generateProxyClass(name, interfaces, (ACC_PUBLIC | ACC_FINAL | ACC_SUPER)); - } - - /** - * Generate a proxy class given a name and a list of proxy interfaces. - * - * @param name the class name of the proxy class - * @param interfaces proxy interfaces - * @param accessFlags access flags of the proxy class - */ - static byte[] generateProxyClass(final String name, - Class[] interfaces, - int accessFlags) - { - ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); - final byte[] classFile = gen.generateClassFile(); - - if (saveGeneratedFiles) { - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Void run() { - try { - int i = name.lastIndexOf('.'); - Path path; - if (i > 0) { - Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar)); - Files.createDirectories(dir); - path = dir.resolve(name.substring(i+1, name.length()) + ".class"); - } else { - path = Path.of(name + ".class"); - } - Files.write(path, classFile); - return null; - } catch (IOException e) { - throw new InternalError( - "I/O exception saving generated file: " + e); - } - } - }); - } - - return classFile; - } - - /* preloaded Method objects for methods in java.lang.Object */ - private static Method hashCodeMethod; - private static Method equalsMethod; - private static Method toStringMethod; - static { - try { - hashCodeMethod = Object.class.getMethod("hashCode"); - equalsMethod = - Object.class.getMethod("equals", new Class[] { Object.class }); - toStringMethod = Object.class.getMethod("toString"); - } catch (NoSuchMethodException e) { - throw new NoSuchMethodError(e.getMessage()); - } - } - - /** name of proxy class */ - private String className; - - /** proxy interfaces */ - private Class[] interfaces; - - /** proxy class access flags */ - private int accessFlags; - - /** constant pool of class being generated */ - private ConstantPool cp = new ConstantPool(); - - /** FieldInfo struct for each field of generated class */ - private List fields = new ArrayList<>(); - - /** MethodInfo struct for each method of generated class */ - private List methods = new ArrayList<>(); - - /** - * maps method signature string to list of ProxyMethod objects for - * proxy methods with that signature - */ - private Map> proxyMethods = new HashMap<>(); - - /** count of ProxyMethod objects added to proxyMethods */ - private int proxyMethodCount = 0; - - /** - * Construct a ProxyGenerator to generate a proxy class with the - * specified name and for the given interfaces. - * - * A ProxyGenerator object contains the state for the ongoing - * generation of a particular proxy class. - */ - private ProxyGenerator(String className, Class[] interfaces, int accessFlags) { - this.className = className; - this.interfaces = interfaces; - this.accessFlags = accessFlags; - } - - /** - * Generate a class file for the proxy class. This method drives the - * class file generation process. - */ - private byte[] generateClassFile() { - - /* ============================================================ - * Step 1: Assemble ProxyMethod objects for all methods to - * generate proxy dispatching code for. - */ - - /* - * Record that proxy methods are needed for the hashCode, equals, - * and toString methods of java.lang.Object. This is done before - * the methods from the proxy interfaces so that the methods from - * java.lang.Object take precedence over duplicate methods in the - * proxy interfaces. - */ - addProxyMethod(hashCodeMethod, Object.class); - addProxyMethod(equalsMethod, Object.class); - addProxyMethod(toStringMethod, Object.class); - - /* - * Now record all of the methods from the proxy interfaces, giving - * earlier interfaces precedence over later ones with duplicate - * methods. - */ - for (Class intf : interfaces) { - for (Method m : intf.getMethods()) { - if (!Modifier.isStatic(m.getModifiers())) { - addProxyMethod(m, intf); - } - } - } - - /* - * For each set of proxy methods with the same signature, - * verify that the methods' return types are compatible. - */ - for (List sigmethods : proxyMethods.values()) { - checkReturnTypes(sigmethods); - } - - /* ============================================================ - * Step 2: Assemble FieldInfo and MethodInfo structs for all of - * fields and methods in the class we are generating. - */ - try { - methods.add(generateConstructor()); - - for (List sigmethods : proxyMethods.values()) { - for (ProxyMethod pm : sigmethods) { - - // add static field for method's Method object - fields.add(new FieldInfo(pm.methodFieldName, - "Ljava/lang/reflect/Method;", - ACC_PRIVATE | ACC_STATIC)); - - // generate code for proxy method and add it - methods.add(pm.generateMethod()); - } - } - - methods.add(generateStaticInitializer()); - - } catch (IOException e) { - throw new InternalError("unexpected I/O Exception", e); - } - - if (methods.size() > 65535) { - throw new IllegalArgumentException("method limit exceeded"); - } - if (fields.size() > 65535) { - throw new IllegalArgumentException("field limit exceeded"); - } - - /* ============================================================ - * Step 3: Write the final class file. - */ - - /* - * Make sure that constant pool indexes are reserved for the - * following items before starting to write the final class file. - */ - cp.getClass(dotToSlash(className)); - cp.getClass(superclassName); - for (Class intf: interfaces) { - cp.getClass(dotToSlash(intf.getName())); - } - - /* - * Disallow new constant pool additions beyond this point, since - * we are about to write the final constant pool table. - */ - cp.setReadOnly(); - - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(bout); - - try { - /* - * Write all the items of the "ClassFile" structure. - * See JVMS section 4.1. - */ - // u4 magic; - dout.writeInt(0xCAFEBABE); - // u2 minor_version; - dout.writeShort(CLASSFILE_MINOR_VERSION); - // u2 major_version; - dout.writeShort(CLASSFILE_MAJOR_VERSION); - - cp.write(dout); // (write constant pool) - - // u2 access_flags; - dout.writeShort(accessFlags); - // u2 this_class; - dout.writeShort(cp.getClass(dotToSlash(className))); - // u2 super_class; - dout.writeShort(cp.getClass(superclassName)); - - // u2 interfaces_count; - dout.writeShort(interfaces.length); - // u2 interfaces[interfaces_count]; - for (Class intf : interfaces) { - dout.writeShort(cp.getClass( - dotToSlash(intf.getName()))); - } - - // u2 fields_count; - dout.writeShort(fields.size()); - // field_info fields[fields_count]; - for (FieldInfo f : fields) { - f.write(dout); - } - - // u2 methods_count; - dout.writeShort(methods.size()); - // method_info methods[methods_count]; - for (MethodInfo m : methods) { - m.write(dout); - } - - // u2 attributes_count; - dout.writeShort(0); // (no ClassFile attributes for proxy classes) - - } catch (IOException e) { - throw new InternalError("unexpected I/O Exception", e); - } - - return bout.toByteArray(); - } - - /** - * Add another method to be proxied, either by creating a new - * ProxyMethod object or augmenting an old one for a duplicate - * method. - * - * "fromClass" indicates the proxy interface that the method was - * found through, which may be different from (a subinterface of) - * the method's "declaring class". Note that the first Method - * object passed for a given name and descriptor identifies the - * Method object (and thus the declaring class) that will be - * passed to the invocation handler's "invoke" method for a given - * set of duplicate methods. - */ - private void addProxyMethod(Method m, Class fromClass) { - String name = m.getName(); - Class[] parameterTypes = m.getParameterTypes(); - Class returnType = m.getReturnType(); - Class[] exceptionTypes = m.getExceptionTypes(); - - String sig = name + getParameterDescriptors(parameterTypes); - List sigmethods = proxyMethods.get(sig); - if (sigmethods != null) { - for (ProxyMethod pm : sigmethods) { - if (returnType == pm.returnType) { - /* - * Found a match: reduce exception types to the - * greatest set of exceptions that can thrown - * compatibly with the throws clauses of both - * overridden methods. - */ - List> legalExceptions = new ArrayList<>(); - collectCompatibleTypes( - exceptionTypes, pm.exceptionTypes, legalExceptions); - collectCompatibleTypes( - pm.exceptionTypes, exceptionTypes, legalExceptions); - pm.exceptionTypes = new Class[legalExceptions.size()]; - pm.exceptionTypes = - legalExceptions.toArray(pm.exceptionTypes); - return; - } - } - } else { - sigmethods = new ArrayList<>(3); - proxyMethods.put(sig, sigmethods); - } - sigmethods.add(new ProxyMethod(name, parameterTypes, returnType, - exceptionTypes, fromClass)); - } - - /** - * For a given set of proxy methods with the same signature, check - * that their return types are compatible according to the Proxy - * specification. - * - * Specifically, if there is more than one such method, then all - * of the return types must be reference types, and there must be - * one return type that is assignable to each of the rest of them. - */ - private static void checkReturnTypes(List methods) { - /* - * If there is only one method with a given signature, there - * cannot be a conflict. This is the only case in which a - * primitive (or void) return type is allowed. - */ - if (methods.size() < 2) { - return; - } - - /* - * List of return types that are not yet known to be - * assignable from ("covered" by) any of the others. - */ - LinkedList> uncoveredReturnTypes = new LinkedList<>(); - - nextNewReturnType: - for (ProxyMethod pm : methods) { - Class newReturnType = pm.returnType; - if (newReturnType.isPrimitive()) { - throw new IllegalArgumentException( - "methods with same signature " + - getFriendlyMethodSignature(pm.methodName, - pm.parameterTypes) + - " but incompatible return types: " + - newReturnType.getName() + " and others"); - } - boolean added = false; - - /* - * Compare the new return type to the existing uncovered - * return types. - */ - ListIterator> liter = uncoveredReturnTypes.listIterator(); - while (liter.hasNext()) { - Class uncoveredReturnType = liter.next(); - - /* - * If an existing uncovered return type is assignable - * to this new one, then we can forget the new one. - */ - if (newReturnType.isAssignableFrom(uncoveredReturnType)) { - assert !added; - continue nextNewReturnType; - } - - /* - * If the new return type is assignable to an existing - * uncovered one, then should replace the existing one - * with the new one (or just forget the existing one, - * if the new one has already be put in the list). - */ - if (uncoveredReturnType.isAssignableFrom(newReturnType)) { - // (we can assume that each return type is unique) - if (!added) { - liter.set(newReturnType); - added = true; - } else { - liter.remove(); - } - } - } - - /* - * If we got through the list of existing uncovered return - * types without an assignability relationship, then add - * the new return type to the list of uncovered ones. - */ - if (!added) { - uncoveredReturnTypes.add(newReturnType); - } - } - - /* - * We shouldn't end up with more than one return type that is - * not assignable from any of the others. - */ - if (uncoveredReturnTypes.size() > 1) { - ProxyMethod pm = methods.get(0); - throw new IllegalArgumentException( - "methods with same signature " + - getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) + - " but incompatible return types: " + uncoveredReturnTypes); - } - } - - /** - * A FieldInfo object contains information about a particular field - * in the class being generated. The class mirrors the data items of - * the "field_info" structure of the class file format (see JVMS 4.5). - */ - private class FieldInfo { - public int accessFlags; - public String name; - public String descriptor; - - public FieldInfo(String name, String descriptor, int accessFlags) { - this.name = name; - this.descriptor = descriptor; - this.accessFlags = accessFlags; - - /* - * Make sure that constant pool indexes are reserved for the - * following items before starting to write the final class file. - */ - cp.getUtf8(name); - cp.getUtf8(descriptor); - } - - public void write(DataOutputStream out) throws IOException { - /* - * Write all the items of the "field_info" structure. - * See JVMS section 4.5. - */ - // u2 access_flags; - out.writeShort(accessFlags); - // u2 name_index; - out.writeShort(cp.getUtf8(name)); - // u2 descriptor_index; - out.writeShort(cp.getUtf8(descriptor)); - // u2 attributes_count; - out.writeShort(0); // (no field_info attributes for proxy classes) - } - } - - /** - * An ExceptionTableEntry object holds values for the data items of - * an entry in the "exception_table" item of the "Code" attribute of - * "method_info" structures (see JVMS 4.7.3). - */ - private static class ExceptionTableEntry { - public short startPc; - public short endPc; - public short handlerPc; - public short catchType; - - public ExceptionTableEntry(short startPc, short endPc, - short handlerPc, short catchType) - { - this.startPc = startPc; - this.endPc = endPc; - this.handlerPc = handlerPc; - this.catchType = catchType; - } - }; - - /** - * A MethodInfo object contains information about a particular method - * in the class being generated. This class mirrors the data items of - * the "method_info" structure of the class file format (see JVMS 4.6). - */ - private class MethodInfo { - public int accessFlags; - public String name; - public String descriptor; - public short maxStack; - public short maxLocals; - public ByteArrayOutputStream code = new ByteArrayOutputStream(); - public List exceptionTable = - new ArrayList(); - public short[] declaredExceptions; - - public MethodInfo(String name, String descriptor, int accessFlags) { - this.name = name; - this.descriptor = descriptor; - this.accessFlags = accessFlags; - - /* - * Make sure that constant pool indexes are reserved for the - * following items before starting to write the final class file. - */ - cp.getUtf8(name); - cp.getUtf8(descriptor); - cp.getUtf8("Code"); - cp.getUtf8("Exceptions"); - } - - public void write(DataOutputStream out) throws IOException { - /* - * Write all the items of the "method_info" structure. - * See JVMS section 4.6. - */ - // u2 access_flags; - out.writeShort(accessFlags); - // u2 name_index; - out.writeShort(cp.getUtf8(name)); - // u2 descriptor_index; - out.writeShort(cp.getUtf8(descriptor)); - // u2 attributes_count; - out.writeShort(2); // (two method_info attributes:) - - // Write "Code" attribute. See JVMS section 4.7.3. - - // u2 attribute_name_index; - out.writeShort(cp.getUtf8("Code")); - // u4 attribute_length; - out.writeInt(12 + code.size() + 8 * exceptionTable.size()); - // u2 max_stack; - out.writeShort(maxStack); - // u2 max_locals; - out.writeShort(maxLocals); - // u2 code_length; - out.writeInt(code.size()); - // u1 code[code_length]; - code.writeTo(out); - // u2 exception_table_length; - out.writeShort(exceptionTable.size()); - for (ExceptionTableEntry e : exceptionTable) { - // u2 start_pc; - out.writeShort(e.startPc); - // u2 end_pc; - out.writeShort(e.endPc); - // u2 handler_pc; - out.writeShort(e.handlerPc); - // u2 catch_type; - out.writeShort(e.catchType); - } - // u2 attributes_count; - out.writeShort(0); - - // write "Exceptions" attribute. See JVMS section 4.7.4. - - // u2 attribute_name_index; - out.writeShort(cp.getUtf8("Exceptions")); - // u4 attributes_length; - out.writeInt(2 + 2 * declaredExceptions.length); - // u2 number_of_exceptions; - out.writeShort(declaredExceptions.length); - // u2 exception_index_table[number_of_exceptions]; - for (short value : declaredExceptions) { - out.writeShort(value); - } - } - - } - - /** - * A ProxyMethod object represents a proxy method in the proxy class - * being generated: a method whose implementation will encode and - * dispatch invocations to the proxy instance's invocation handler. - */ - private class ProxyMethod { - - public String methodName; - public Class[] parameterTypes; - public Class returnType; - public Class[] exceptionTypes; - public Class fromClass; - public String methodFieldName; - - private ProxyMethod(String methodName, Class[] parameterTypes, - Class returnType, Class[] exceptionTypes, - Class fromClass) - { - this.methodName = methodName; - this.parameterTypes = parameterTypes; - this.returnType = returnType; - this.exceptionTypes = exceptionTypes; - this.fromClass = fromClass; - this.methodFieldName = "m" + proxyMethodCount++; - } - - /** - * Return a MethodInfo object for this method, including generating - * the code and exception table entry. - */ - private MethodInfo generateMethod() throws IOException { - String desc = getMethodDescriptor(parameterTypes, returnType); - MethodInfo minfo = new MethodInfo(methodName, desc, - ACC_PUBLIC | ACC_FINAL); - - int[] parameterSlot = new int[parameterTypes.length]; - int nextSlot = 1; - for (int i = 0; i < parameterSlot.length; i++) { - parameterSlot[i] = nextSlot; - nextSlot += getWordsPerType(parameterTypes[i]); - } - int localSlot0 = nextSlot; - short pc, tryBegin = 0, tryEnd; - - DataOutputStream out = new DataOutputStream(minfo.code); - - code_aload(0, out); - - out.writeByte(opc_getfield); - out.writeShort(cp.getFieldRef( - superclassName, - handlerFieldName, "Ljava/lang/reflect/InvocationHandler;")); - - code_aload(0, out); - - out.writeByte(opc_getstatic); - out.writeShort(cp.getFieldRef( - dotToSlash(className), - methodFieldName, "Ljava/lang/reflect/Method;")); - - if (parameterTypes.length > 0) { - - code_ipush(parameterTypes.length, out); - - out.writeByte(opc_anewarray); - out.writeShort(cp.getClass("java/lang/Object")); - - for (int i = 0; i < parameterTypes.length; i++) { - - out.writeByte(opc_dup); - - code_ipush(i, out); - - codeWrapArgument(parameterTypes[i], parameterSlot[i], out); - - out.writeByte(opc_aastore); - } - } else { - - out.writeByte(opc_aconst_null); - } - - out.writeByte(opc_invokeinterface); - out.writeShort(cp.getInterfaceMethodRef( - "java/lang/reflect/InvocationHandler", - "invoke", - "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + - "[Ljava/lang/Object;)Ljava/lang/Object;")); - out.writeByte(4); - out.writeByte(0); - - if (returnType == void.class) { - - out.writeByte(opc_pop); - - out.writeByte(opc_return); - - } else { - - codeUnwrapReturnValue(returnType, out); - } - - tryEnd = pc = (short) minfo.code.size(); - - List> catchList = computeUniqueCatchList(exceptionTypes); - if (catchList.size() > 0) { - - for (Class ex : catchList) { - minfo.exceptionTable.add(new ExceptionTableEntry( - tryBegin, tryEnd, pc, - cp.getClass(dotToSlash(ex.getName())))); - } - - out.writeByte(opc_athrow); - - pc = (short) minfo.code.size(); - - minfo.exceptionTable.add(new ExceptionTableEntry( - tryBegin, tryEnd, pc, cp.getClass("java/lang/Throwable"))); - - code_astore(localSlot0, out); - - out.writeByte(opc_new); - out.writeShort(cp.getClass( - "java/lang/reflect/UndeclaredThrowableException")); - - out.writeByte(opc_dup); - - code_aload(localSlot0, out); - - out.writeByte(opc_invokespecial); - - out.writeShort(cp.getMethodRef( - "java/lang/reflect/UndeclaredThrowableException", - "", "(Ljava/lang/Throwable;)V")); - - out.writeByte(opc_athrow); - } - - if (minfo.code.size() > 65535) { - throw new IllegalArgumentException("code size limit exceeded"); - } - - minfo.maxStack = 10; - minfo.maxLocals = (short) (localSlot0 + 1); - minfo.declaredExceptions = new short[exceptionTypes.length]; - for (int i = 0; i < exceptionTypes.length; i++) { - minfo.declaredExceptions[i] = cp.getClass( - dotToSlash(exceptionTypes[i].getName())); - } - - return minfo; - } - - /** - * Generate code for wrapping an argument of the given type - * whose value can be found at the specified local variable - * index, in order for it to be passed (as an Object) to the - * invocation handler's "invoke" method. The code is written - * to the supplied stream. - */ - private void codeWrapArgument(Class type, int slot, - DataOutputStream out) - throws IOException - { - if (type.isPrimitive()) { - PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); - - if (type == int.class || - type == boolean.class || - type == byte.class || - type == char.class || - type == short.class) - { - code_iload(slot, out); - } else if (type == long.class) { - code_lload(slot, out); - } else if (type == float.class) { - code_fload(slot, out); - } else if (type == double.class) { - code_dload(slot, out); - } else { - throw new AssertionError(); - } - - out.writeByte(opc_invokestatic); - out.writeShort(cp.getMethodRef( - prim.wrapperClassName, - "valueOf", prim.wrapperValueOfDesc)); - - } else { - - code_aload(slot, out); - } - } - - /** - * Generate code for unwrapping a return value of the given - * type from the invocation handler's "invoke" method (as type - * Object) to its correct type. The code is written to the - * supplied stream. - */ - private void codeUnwrapReturnValue(Class type, DataOutputStream out) - throws IOException - { - if (type.isPrimitive()) { - PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); - - out.writeByte(opc_checkcast); - out.writeShort(cp.getClass(prim.wrapperClassName)); - - out.writeByte(opc_invokevirtual); - out.writeShort(cp.getMethodRef( - prim.wrapperClassName, - prim.unwrapMethodName, prim.unwrapMethodDesc)); - - if (type == int.class || - type == boolean.class || - type == byte.class || - type == char.class || - type == short.class) - { - out.writeByte(opc_ireturn); - } else if (type == long.class) { - out.writeByte(opc_lreturn); - } else if (type == float.class) { - out.writeByte(opc_freturn); - } else if (type == double.class) { - out.writeByte(opc_dreturn); - } else { - throw new AssertionError(); - } - - } else { - - out.writeByte(opc_checkcast); - out.writeShort(cp.getClass(dotToSlash(type.getName()))); - - out.writeByte(opc_areturn); - } - } - - /** - * Generate code for initializing the static field that stores - * the Method object for this proxy method. The code is written - * to the supplied stream. - */ - private void codeFieldInitialization(DataOutputStream out) - throws IOException - { - codeClassForName(fromClass, out); - - code_ldc(cp.getString(methodName), out); - - code_ipush(parameterTypes.length, out); - - out.writeByte(opc_anewarray); - out.writeShort(cp.getClass("java/lang/Class")); - - for (int i = 0; i < parameterTypes.length; i++) { - - out.writeByte(opc_dup); - - code_ipush(i, out); - - if (parameterTypes[i].isPrimitive()) { - PrimitiveTypeInfo prim = - PrimitiveTypeInfo.get(parameterTypes[i]); - - out.writeByte(opc_getstatic); - out.writeShort(cp.getFieldRef( - prim.wrapperClassName, "TYPE", "Ljava/lang/Class;")); - - } else { - codeClassForName(parameterTypes[i], out); - } - - out.writeByte(opc_aastore); - } - - out.writeByte(opc_invokevirtual); - out.writeShort(cp.getMethodRef( - "java/lang/Class", - "getMethod", - "(Ljava/lang/String;[Ljava/lang/Class;)" + - "Ljava/lang/reflect/Method;")); - - out.writeByte(opc_putstatic); - out.writeShort(cp.getFieldRef( - dotToSlash(className), - methodFieldName, "Ljava/lang/reflect/Method;")); - } - } - - /** - * Generate the constructor method for the proxy class. - */ - private MethodInfo generateConstructor() throws IOException { - MethodInfo minfo = new MethodInfo( - "", "(Ljava/lang/reflect/InvocationHandler;)V", - ACC_PUBLIC); - - DataOutputStream out = new DataOutputStream(minfo.code); - - code_aload(0, out); - - code_aload(1, out); - - out.writeByte(opc_invokespecial); - out.writeShort(cp.getMethodRef( - superclassName, - "", "(Ljava/lang/reflect/InvocationHandler;)V")); - - out.writeByte(opc_return); - - minfo.maxStack = 10; - minfo.maxLocals = 2; - minfo.declaredExceptions = new short[0]; - - return minfo; - } - - /** - * Generate the static initializer method for the proxy class. - */ - private MethodInfo generateStaticInitializer() throws IOException { - MethodInfo minfo = new MethodInfo( - "", "()V", ACC_STATIC); - - int localSlot0 = 1; - short pc, tryBegin = 0, tryEnd; - - DataOutputStream out = new DataOutputStream(minfo.code); - - for (List sigmethods : proxyMethods.values()) { - for (ProxyMethod pm : sigmethods) { - pm.codeFieldInitialization(out); - } - } - - out.writeByte(opc_return); - - tryEnd = pc = (short) minfo.code.size(); - - minfo.exceptionTable.add(new ExceptionTableEntry( - tryBegin, tryEnd, pc, - cp.getClass("java/lang/NoSuchMethodException"))); - - code_astore(localSlot0, out); - - out.writeByte(opc_new); - out.writeShort(cp.getClass("java/lang/NoSuchMethodError")); - - out.writeByte(opc_dup); - - code_aload(localSlot0, out); - - out.writeByte(opc_invokevirtual); - out.writeShort(cp.getMethodRef( - "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); - - out.writeByte(opc_invokespecial); - out.writeShort(cp.getMethodRef( - "java/lang/NoSuchMethodError", "", "(Ljava/lang/String;)V")); - - out.writeByte(opc_athrow); - - pc = (short) minfo.code.size(); - - minfo.exceptionTable.add(new ExceptionTableEntry( - tryBegin, tryEnd, pc, - cp.getClass("java/lang/ClassNotFoundException"))); - - code_astore(localSlot0, out); - - out.writeByte(opc_new); - out.writeShort(cp.getClass("java/lang/NoClassDefFoundError")); - - out.writeByte(opc_dup); - - code_aload(localSlot0, out); - - out.writeByte(opc_invokevirtual); - out.writeShort(cp.getMethodRef( - "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); - - out.writeByte(opc_invokespecial); - out.writeShort(cp.getMethodRef( - "java/lang/NoClassDefFoundError", - "", "(Ljava/lang/String;)V")); - - out.writeByte(opc_athrow); - - if (minfo.code.size() > 65535) { - throw new IllegalArgumentException("code size limit exceeded"); - } - - minfo.maxStack = 10; - minfo.maxLocals = (short) (localSlot0 + 1); - minfo.declaredExceptions = new short[0]; - - return minfo; - } - - - /* - * =============== Code Generation Utility Methods =============== - */ - - /* - * The following methods generate code for the load or store operation - * indicated by their name for the given local variable. The code is - * written to the supplied stream. - */ - - private void code_iload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_iload, opc_iload_0, out); - } - - private void code_lload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_lload, opc_lload_0, out); - } - - private void code_fload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_fload, opc_fload_0, out); - } - - private void code_dload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_dload, opc_dload_0, out); - } - - private void code_aload(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_aload, opc_aload_0, out); - } - -// private void code_istore(int lvar, DataOutputStream out) -// throws IOException -// { -// codeLocalLoadStore(lvar, opc_istore, opc_istore_0, out); -// } - -// private void code_lstore(int lvar, DataOutputStream out) -// throws IOException -// { -// codeLocalLoadStore(lvar, opc_lstore, opc_lstore_0, out); -// } - -// private void code_fstore(int lvar, DataOutputStream out) -// throws IOException -// { -// codeLocalLoadStore(lvar, opc_fstore, opc_fstore_0, out); -// } - -// private void code_dstore(int lvar, DataOutputStream out) -// throws IOException -// { -// codeLocalLoadStore(lvar, opc_dstore, opc_dstore_0, out); -// } - - private void code_astore(int lvar, DataOutputStream out) - throws IOException - { - codeLocalLoadStore(lvar, opc_astore, opc_astore_0, out); - } - - /** - * Generate code for a load or store instruction for the given local - * variable. The code is written to the supplied stream. - * - * "opcode" indicates the opcode form of the desired load or store - * instruction that takes an explicit local variable index, and - * "opcode_0" indicates the corresponding form of the instruction - * with the implicit index 0. - */ - private void codeLocalLoadStore(int lvar, int opcode, int opcode_0, - DataOutputStream out) - throws IOException - { - assert lvar >= 0 && lvar <= 0xFFFF; - if (lvar <= 3) { - out.writeByte(opcode_0 + lvar); - } else if (lvar <= 0xFF) { - out.writeByte(opcode); - out.writeByte(lvar & 0xFF); - } else { - /* - * Use the "wide" instruction modifier for local variable - * indexes that do not fit into an unsigned byte. - */ - out.writeByte(opc_wide); - out.writeByte(opcode); - out.writeShort(lvar & 0xFFFF); - } - } - - /** - * Generate code for an "ldc" instruction for the given constant pool - * index (the "ldc_w" instruction is used if the index does not fit - * into an unsigned byte). The code is written to the supplied stream. - */ - private void code_ldc(int index, DataOutputStream out) - throws IOException - { - assert index >= 0 && index <= 0xFFFF; - if (index <= 0xFF) { - out.writeByte(opc_ldc); - out.writeByte(index & 0xFF); - } else { - out.writeByte(opc_ldc_w); - out.writeShort(index & 0xFFFF); - } - } - - /** - * Generate code to push a constant integer value on to the operand - * stack, using the "iconst_", "bipush", or "sipush" instructions - * depending on the size of the value. The code is written to the - * supplied stream. - */ - private void code_ipush(int value, DataOutputStream out) - throws IOException - { - if (value >= -1 && value <= 5) { - out.writeByte(opc_iconst_0 + value); - } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { - out.writeByte(opc_bipush); - out.writeByte(value & 0xFF); - } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { - out.writeByte(opc_sipush); - out.writeShort(value & 0xFFFF); - } else { - throw new AssertionError(); - } - } - - /** - * Generate code to invoke the Class.forName with the name of the given - * class to get its Class object at runtime. The code is written to - * the supplied stream. Note that the code generated by this method - * may cause the checked ClassNotFoundException to be thrown. - */ - private void codeClassForName(Class cl, DataOutputStream out) - throws IOException - { - code_ldc(cp.getString(cl.getName()), out); - - out.writeByte(opc_invokestatic); - out.writeShort(cp.getMethodRef( - "java/lang/Class", - "forName", "(Ljava/lang/String;)Ljava/lang/Class;")); - } - - - /* - * ==================== General Utility Methods ==================== - */ - - /** - * 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 4.2). - */ - private static String dotToSlash(String name) { - return name.replace('.', '/'); - } - - /** - * Return the "method descriptor" string for a method with the given - * parameter types and return type. See JVMS section 4.3.3. - */ - private static String getMethodDescriptor(Class[] parameterTypes, - Class returnType) - { - return getParameterDescriptors(parameterTypes) + - ((returnType == void.class) ? "V" : getFieldType(returnType)); - } - - /** - * Return the list of "parameter descriptor" strings enclosed in - * parentheses corresponding to the given parameter types (in other - * words, a method descriptor without a return descriptor). This - * string is useful for constructing string keys for methods without - * regard to their return type. - */ - private static String getParameterDescriptors(Class[] parameterTypes) { - StringBuilder desc = new StringBuilder("("); - for (int i = 0; i < parameterTypes.length; i++) { - desc.append(getFieldType(parameterTypes[i])); - } - desc.append(')'); - return desc.toString(); - } - - /** - * Return the "field type" string for the given type, appropriate for - * a field descriptor, a parameter descriptor, or a return descriptor - * other than "void". See JVMS section 4.3.2. - */ - private static String getFieldType(Class type) { - if (type.isPrimitive()) { - return PrimitiveTypeInfo.get(type).baseTypeString; - } else if (type.isArray()) { - /* - * According to JLS 20.3.2, the getName() method on Class does - * return the VM type descriptor format for array classes (only); - * using that should be quicker than the otherwise obvious code: - * - * return "[" + getTypeDescriptor(type.getComponentType()); - */ - return type.getName().replace('.', '/'); - } else { - return "L" + dotToSlash(type.getName()) + ";"; - } - } - - /** - * Returns a human-readable string representing the signature of a - * method with the given name and parameter types. - */ - private static String getFriendlyMethodSignature(String name, - Class[] parameterTypes) - { - StringBuilder sig = new StringBuilder(name); - sig.append('('); - for (int i = 0; i < parameterTypes.length; i++) { - if (i > 0) { - sig.append(','); - } - Class parameterType = parameterTypes[i]; - int dimensions = 0; - while (parameterType.isArray()) { - parameterType = parameterType.getComponentType(); - dimensions++; - } - sig.append(parameterType.getName()); - while (dimensions-- > 0) { - sig.append("[]"); - } - } - sig.append(')'); - return sig.toString(); - } - - /** - * Return the number of abstract "words", or consecutive local variable - * indexes, required to contain a value of the given type. See JVMS - * section 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 - * least one of the types in the "with" array. - * - * This method is useful for computing the greatest common set of - * declared exceptions from duplicate methods inherited from - * different interfaces. - */ - private static void collectCompatibleTypes(Class[] from, - Class[] with, - List> list) - { - for (Class fc: from) { - if (!list.contains(fc)) { - for (Class wc: with) { - if (wc.isAssignableFrom(fc)) { - list.add(fc); - break; - } - } - } - } - } - - /** - * Given the exceptions declared in the throws clause of a proxy method, - * compute the exceptions that need to be caught from the invocation - * handler's invoke method and rethrown intact in the method's - * implementation before catching other Throwables and wrapping them - * in UndeclaredThrowableExceptions. - * - * The exceptions to be caught are returned in a List object. Each - * exception in the returned list is guaranteed to not be a subclass of - * any of the other exceptions in the list, so the catch blocks for - * these exceptions may be generated in any order relative to each other. - * - * Error and RuntimeException are each always contained by the returned - * list (if none of their superclasses are contained), since those - * unchecked exceptions should always be rethrown intact, and thus their - * subclasses will never appear in the returned list. - * - * The returned List will be empty if java.lang.Throwable is in the - * given list of declared exceptions, indicating that no exceptions - * need to be caught. - */ - private static List> computeUniqueCatchList(Class[] exceptions) { - List> uniqueList = new ArrayList<>(); - // unique exceptions to catch - - uniqueList.add(Error.class); // always catch/rethrow these - uniqueList.add(RuntimeException.class); - - nextException: - for (Class ex: exceptions) { - if (ex.isAssignableFrom(Throwable.class)) { - /* - * If Throwable is declared to be thrown by the proxy method, - * then no catch blocks are necessary, because the invoke - * can, at most, throw Throwable anyway. - */ - uniqueList.clear(); - break; - } else if (!Throwable.class.isAssignableFrom(ex)) { - /* - * Ignore types that cannot be thrown by the invoke method. - */ - continue; - } - /* - * Compare this exception against the current list of - * exceptions that need to be caught: - */ - for (int j = 0; j < uniqueList.size();) { - Class ex2 = uniqueList.get(j); - if (ex2.isAssignableFrom(ex)) { - /* - * if a superclass of this exception is already on - * the list to catch, then ignore this one and continue; - */ - continue nextException; - } else if (ex.isAssignableFrom(ex2)) { - /* - * if a subclass of this exception is on the list - * to catch, then remove it; - */ - uniqueList.remove(j); - } else { - j++; // else continue comparing. - } - } - // This exception is unique (so far): add it to the list to catch. - uniqueList.add(ex); - } - return uniqueList; - } - - /** - * A PrimitiveTypeInfo object contains assorted information about - * a primitive type in its public fields. The struct for a particular - * primitive type can be obtained using the static "get" method. - */ - private static class PrimitiveTypeInfo { - - /** "base type" used in various descriptors (see JVMS section 4.3.2) */ - public String baseTypeString; - - /** name of corresponding wrapper class */ - public String wrapperClassName; - - /** method descriptor for wrapper class "valueOf" factory method */ - public String wrapperValueOfDesc; - - /** name of wrapper class method for retrieving primitive value */ - public String unwrapMethodName; - - /** descriptor of same method */ - public String unwrapMethodDesc; - - private static Map,PrimitiveTypeInfo> table = new HashMap<>(); - static { - add(byte.class, Byte.class); - add(char.class, Character.class); - add(double.class, Double.class); - add(float.class, Float.class); - add(int.class, Integer.class); - add(long.class, Long.class); - add(short.class, Short.class); - add(boolean.class, Boolean.class); - } - - private static void add(Class primitiveClass, Class wrapperClass) { - table.put(primitiveClass, - new PrimitiveTypeInfo(primitiveClass, wrapperClass)); - } - - private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) { - assert primitiveClass.isPrimitive(); - - baseTypeString = - Array.newInstance(primitiveClass, 0) - .getClass().getName().substring(1); - wrapperClassName = dotToSlash(wrapperClass.getName()); - wrapperValueOfDesc = - "(" + baseTypeString + ")L" + wrapperClassName + ";"; - unwrapMethodName = primitiveClass.getName() + "Value"; - unwrapMethodDesc = "()" + baseTypeString; - } - - public static PrimitiveTypeInfo get(Class cl) { - return table.get(cl); - } - } - - - /** - * A ConstantPool object represents the constant pool of a class file - * being generated. This representation of a constant pool is designed - * specifically for use by ProxyGenerator; in particular, it assumes - * that constant pool entries will not need to be resorted (for example, - * by their type, as the Java compiler does), so that the final index - * value can be assigned and used when an entry is first created. - * - * Note that new entries cannot be created after the constant pool has - * been written to a class file. To prevent such logic errors, a - * ConstantPool instance can be marked "read only", so that further - * attempts to add new entries will fail with a runtime exception. - * - * See JVMS section 4.4 for more information about the constant pool - * of a class file. - */ - private static class ConstantPool { - - /** - * list of constant pool entries, in constant pool index order. - * - * This list is used when writing the constant pool to a stream - * and for assigning the next index value. Note that element 0 - * of this list corresponds to constant pool index 1. - */ - private List pool = new ArrayList<>(32); - - /** - * maps constant pool data of all types to constant pool indexes. - * - * This map is used to look up the index of an existing entry for - * values of all types. - */ - private Map map = new HashMap<>(16); - - /** true if no new constant pool entries may be added */ - private boolean readOnly = false; - - /** - * Get or assign the index for a CONSTANT_Utf8 entry. - */ - public short getUtf8(String s) { - if (s == null) { - throw new NullPointerException(); - } - return getValue(s); - } - - /** - * Get or assign the index for a CONSTANT_Integer entry. - */ - public short getInteger(int i) { - return getValue(i); - } - - /** - * Get or assign the index for a CONSTANT_Float entry. - */ - public short getFloat(float f) { - return getValue(f); - } - - /** - * Get or assign the index for a CONSTANT_Class entry. - */ - public short getClass(String name) { - short utf8Index = getUtf8(name); - return getIndirect(new IndirectEntry( - CONSTANT_CLASS, utf8Index)); - } - - /** - * Get or assign the index for a CONSTANT_String entry. - */ - public short getString(String s) { - short utf8Index = getUtf8(s); - return getIndirect(new IndirectEntry( - CONSTANT_STRING, utf8Index)); - } - - /** - * Get or assign the index for a CONSTANT_FieldRef entry. - */ - public short getFieldRef(String className, - String name, String descriptor) - { - short classIndex = getClass(className); - short nameAndTypeIndex = getNameAndType(name, descriptor); - return getIndirect(new IndirectEntry( - CONSTANT_FIELD, classIndex, nameAndTypeIndex)); - } - - /** - * Get or assign the index for a CONSTANT_MethodRef entry. - */ - public short getMethodRef(String className, - String name, String descriptor) - { - short classIndex = getClass(className); - short nameAndTypeIndex = getNameAndType(name, descriptor); - return getIndirect(new IndirectEntry( - CONSTANT_METHOD, classIndex, nameAndTypeIndex)); - } - - /** - * Get or assign the index for a CONSTANT_InterfaceMethodRef entry. - */ - public short getInterfaceMethodRef(String className, String name, - String descriptor) - { - short classIndex = getClass(className); - short nameAndTypeIndex = getNameAndType(name, descriptor); - return getIndirect(new IndirectEntry( - CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex)); - } - - /** - * Get or assign the index for a CONSTANT_NameAndType entry. - */ - public short getNameAndType(String name, String descriptor) { - short nameIndex = getUtf8(name); - short descriptorIndex = getUtf8(descriptor); - return getIndirect(new IndirectEntry( - CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex)); - } - - /** - * Set this ConstantPool instance to be "read only". - * - * After this method has been called, further requests to get - * an index for a non-existent entry will cause an InternalError - * to be thrown instead of creating of the entry. - */ - public void setReadOnly() { - readOnly = true; - } - - /** - * Write this constant pool to a stream as part of - * the class file format. - * - * This consists of writing the "constant_pool_count" and - * "constant_pool[]" items of the "ClassFile" structure, as - * described in JVMS section 4.1. - */ - public void write(OutputStream out) throws IOException { - DataOutputStream dataOut = new DataOutputStream(out); - - // constant_pool_count: number of entries plus one - dataOut.writeShort(pool.size() + 1); - - for (Entry e : pool) { - e.write(dataOut); - } - } - - /** - * Add a new constant pool entry and return its index. - */ - private short addEntry(Entry entry) { - pool.add(entry); - /* - * Note that this way of determining the index of the - * added entry is wrong if this pool supports - * CONSTANT_Long or CONSTANT_Double entries. - */ - if (pool.size() >= 65535) { - throw new IllegalArgumentException( - "constant pool size limit exceeded"); - } - return (short) pool.size(); - } - - /** - * Get or assign the index for an entry of a type that contains - * a direct value. The type of the given object determines the - * type of the desired entry as follows: - * - * java.lang.String CONSTANT_Utf8 - * java.lang.Integer CONSTANT_Integer - * java.lang.Float CONSTANT_Float - * java.lang.Long CONSTANT_Long - * java.lang.Double CONSTANT_DOUBLE - */ - private short getValue(Object key) { - Integer index = map.get(key); - if (index != null) { - return index.shortValue(); - } else { - if (readOnly) { - throw new InternalError( - "late constant pool addition: " + key); - } - short i = addEntry(new ValueEntry(key)); - map.put(key, (int)i); - return i; - } - } - - /** - * Get or assign the index for an entry of a type that contains - * references to other constant pool entries. - */ - private short getIndirect(IndirectEntry e) { - Integer index = map.get(e); - if (index != null) { - return index.shortValue(); - } else { - if (readOnly) { - throw new InternalError("late constant pool addition"); - } - short i = addEntry(e); - map.put(e, (int)i); - return i; - } - } - - /** - * Entry is the abstact superclass of all constant pool entry types - * that can be stored in the "pool" list; its purpose is to define a - * common method for writing constant pool entries to a class file. - */ - private abstract static class Entry { - public abstract void write(DataOutputStream out) - throws IOException; - } - - /** - * ValueEntry represents a constant pool entry of a type that - * contains a direct value (see the comments for the "getValue" - * method for a list of such types). - * - * ValueEntry objects are not used as keys for their entries in the - * Map "map", so no useful hashCode or equals methods are defined. - */ - private static class ValueEntry extends Entry { - private Object value; - - public ValueEntry(Object value) { - this.value = value; - } - - public void write(DataOutputStream out) throws IOException { - if (value instanceof String) { - out.writeByte(CONSTANT_UTF8); - out.writeUTF((String) value); - } else if (value instanceof Integer) { - out.writeByte(CONSTANT_INTEGER); - out.writeInt(((Integer) value).intValue()); - } else if (value instanceof Float) { - out.writeByte(CONSTANT_FLOAT); - out.writeFloat(((Float) value).floatValue()); - } else if (value instanceof Long) { - out.writeByte(CONSTANT_LONG); - out.writeLong(((Long) value).longValue()); - } else if (value instanceof Double) { - out.writeDouble(CONSTANT_DOUBLE); - out.writeDouble(((Double) value).doubleValue()); - } else { - throw new InternalError("bogus value entry: " + value); - } - } - } - - /** - * IndirectEntry represents a constant pool entry of a type that - * references other constant pool entries, i.e., the following types: - * - * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref, - * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and - * CONSTANT_NameAndType. - * - * Each of these entry types contains either one or two indexes of - * other constant pool entries. - * - * IndirectEntry objects are used as the keys for their entries in - * the Map "map", so the hashCode and equals methods are overridden - * to allow matching. - */ - private static class IndirectEntry extends Entry { - private int tag; - private short index0; - private short index1; - - /** - * Construct an IndirectEntry for a constant pool entry type - * that contains one index of another entry. - */ - public IndirectEntry(int tag, short index) { - this.tag = tag; - this.index0 = index; - this.index1 = 0; - } - - /** - * Construct an IndirectEntry for a constant pool entry type - * that contains two indexes for other entries. - */ - public IndirectEntry(int tag, short index0, short index1) { - this.tag = tag; - this.index0 = index0; - this.index1 = index1; - } - - public void write(DataOutputStream out) throws IOException { - out.writeByte(tag); - out.writeShort(index0); - /* - * If this entry type contains two indexes, write - * out the second, too. - */ - if (tag == CONSTANT_FIELD || - tag == CONSTANT_METHOD || - tag == CONSTANT_INTERFACEMETHOD || - tag == CONSTANT_NAMEANDTYPE) - { - out.writeShort(index1); - } - } - - public int hashCode() { - return tag + index0 + index1; - } - - public boolean equals(Object obj) { - if (obj instanceof IndirectEntry) { - IndirectEntry other = (IndirectEntry) obj; - if (tag == other.tag && - index0 == other.index0 && index1 == other.index1) - { - return true; - } - } - return false; - } - } - } -} --- /dev/null 2019-08-16 15:28:57.192077812 -0400 +++ new/src/java.base/share/classes/java/lang/reflect/ProxyGenerator_v49.java 2019-08-21 16:28:42.952030956 -0400 @@ -0,0 +1,2031 @@ +/* + * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import sun.security.action.GetBooleanAction; + +/** + * ProxyGenerator contains the code to generate a dynamic proxy class + * for the java.lang.reflect.Proxy API. + * + * The external interfaces to ProxyGenerator is the static + * "generateProxyClass" method. + * + * @author Peter Jones + * @since 1.3 + */ +class ProxyGenerator_v49 { + /* + * In the comments below, "JVMS" refers to The Java Virtual Machine + * Specification Second Edition and "JLS" refers to the original + * version of The Java Language Specification, unless otherwise + * specified. + */ + + /* generate 1.5-era class file version */ + private static final int CLASSFILE_MAJOR_VERSION = 49; + private static final int CLASSFILE_MINOR_VERSION = 0; + + /* + * beginning of constants copied from + * sun.tools.java.RuntimeConstants (which no longer exists): + */ + + /* constant pool tags */ + private static final int CONSTANT_UTF8 = 1; + private static final int CONSTANT_UNICODE = 2; + private static final int CONSTANT_INTEGER = 3; + private static final int CONSTANT_FLOAT = 4; + private static final int CONSTANT_LONG = 5; + private static final int CONSTANT_DOUBLE = 6; + private static final int CONSTANT_CLASS = 7; + private static final int CONSTANT_STRING = 8; + private static final int CONSTANT_FIELD = 9; + private static final int CONSTANT_METHOD = 10; + private static final int CONSTANT_INTERFACEMETHOD = 11; + private static final int CONSTANT_NAMEANDTYPE = 12; + + /* access and modifier flags */ + private static final int ACC_PUBLIC = 0x00000001; + private static final int ACC_PRIVATE = 0x00000002; +// private static final int ACC_PROTECTED = 0x00000004; + private static final int ACC_STATIC = 0x00000008; + private static final int ACC_FINAL = 0x00000010; +// private static final int ACC_SYNCHRONIZED = 0x00000020; +// private static final int ACC_VOLATILE = 0x00000040; +// private static final int ACC_TRANSIENT = 0x00000080; +// private static final int ACC_NATIVE = 0x00000100; +// private static final int ACC_INTERFACE = 0x00000200; +// private static final int ACC_ABSTRACT = 0x00000400; + private static final int ACC_SUPER = 0x00000020; +// private static final int ACC_STRICT = 0x00000800; + + /* opcodes */ +// private static final int opc_nop = 0; + private static final int opc_aconst_null = 1; +// private static final int opc_iconst_m1 = 2; + private static final int opc_iconst_0 = 3; +// private static final int opc_iconst_1 = 4; +// private static final int opc_iconst_2 = 5; +// private static final int opc_iconst_3 = 6; +// private static final int opc_iconst_4 = 7; +// private static final int opc_iconst_5 = 8; +// private static final int opc_lconst_0 = 9; +// private static final int opc_lconst_1 = 10; +// private static final int opc_fconst_0 = 11; +// private static final int opc_fconst_1 = 12; +// private static final int opc_fconst_2 = 13; +// private static final int opc_dconst_0 = 14; +// private static final int opc_dconst_1 = 15; + private static final int opc_bipush = 16; + private static final int opc_sipush = 17; + private static final int opc_ldc = 18; + private static final int opc_ldc_w = 19; +// private static final int opc_ldc2_w = 20; + private static final int opc_iload = 21; + private static final int opc_lload = 22; + private static final int opc_fload = 23; + private static final int opc_dload = 24; + private static final int opc_aload = 25; + private static final int opc_iload_0 = 26; +// private static final int opc_iload_1 = 27; +// private static final int opc_iload_2 = 28; +// private static final int opc_iload_3 = 29; + private static final int opc_lload_0 = 30; +// private static final int opc_lload_1 = 31; +// private static final int opc_lload_2 = 32; +// private static final int opc_lload_3 = 33; + private static final int opc_fload_0 = 34; +// private static final int opc_fload_1 = 35; +// private static final int opc_fload_2 = 36; +// private static final int opc_fload_3 = 37; + private static final int opc_dload_0 = 38; +// private static final int opc_dload_1 = 39; +// private static final int opc_dload_2 = 40; +// private static final int opc_dload_3 = 41; + private static final int opc_aload_0 = 42; +// private static final int opc_aload_1 = 43; +// private static final int opc_aload_2 = 44; +// private static final int opc_aload_3 = 45; +// private static final int opc_iaload = 46; +// private static final int opc_laload = 47; +// private static final int opc_faload = 48; +// private static final int opc_daload = 49; +// private static final int opc_aaload = 50; +// private static final int opc_baload = 51; +// private static final int opc_caload = 52; +// private static final int opc_saload = 53; +// private static final int opc_istore = 54; +// private static final int opc_lstore = 55; +// private static final int opc_fstore = 56; +// private static final int opc_dstore = 57; + private static final int opc_astore = 58; +// private static final int opc_istore_0 = 59; +// private static final int opc_istore_1 = 60; +// private static final int opc_istore_2 = 61; +// private static final int opc_istore_3 = 62; +// private static final int opc_lstore_0 = 63; +// private static final int opc_lstore_1 = 64; +// private static final int opc_lstore_2 = 65; +// private static final int opc_lstore_3 = 66; +// private static final int opc_fstore_0 = 67; +// private static final int opc_fstore_1 = 68; +// private static final int opc_fstore_2 = 69; +// private static final int opc_fstore_3 = 70; +// private static final int opc_dstore_0 = 71; +// private static final int opc_dstore_1 = 72; +// private static final int opc_dstore_2 = 73; +// private static final int opc_dstore_3 = 74; + private static final int opc_astore_0 = 75; +// private static final int opc_astore_1 = 76; +// private static final int opc_astore_2 = 77; +// private static final int opc_astore_3 = 78; +// private static final int opc_iastore = 79; +// private static final int opc_lastore = 80; +// private static final int opc_fastore = 81; +// private static final int opc_dastore = 82; + private static final int opc_aastore = 83; +// private static final int opc_bastore = 84; +// private static final int opc_castore = 85; +// private static final int opc_sastore = 86; + private static final int opc_pop = 87; +// private static final int opc_pop2 = 88; + private static final int opc_dup = 89; +// private static final int opc_dup_x1 = 90; +// private static final int opc_dup_x2 = 91; +// private static final int opc_dup2 = 92; +// private static final int opc_dup2_x1 = 93; +// private static final int opc_dup2_x2 = 94; +// private static final int opc_swap = 95; +// private static final int opc_iadd = 96; +// private static final int opc_ladd = 97; +// private static final int opc_fadd = 98; +// private static final int opc_dadd = 99; +// private static final int opc_isub = 100; +// private static final int opc_lsub = 101; +// private static final int opc_fsub = 102; +// private static final int opc_dsub = 103; +// private static final int opc_imul = 104; +// private static final int opc_lmul = 105; +// private static final int opc_fmul = 106; +// private static final int opc_dmul = 107; +// private static final int opc_idiv = 108; +// private static final int opc_ldiv = 109; +// private static final int opc_fdiv = 110; +// private static final int opc_ddiv = 111; +// private static final int opc_irem = 112; +// private static final int opc_lrem = 113; +// private static final int opc_frem = 114; +// private static final int opc_drem = 115; +// private static final int opc_ineg = 116; +// private static final int opc_lneg = 117; +// private static final int opc_fneg = 118; +// private static final int opc_dneg = 119; +// private static final int opc_ishl = 120; +// private static final int opc_lshl = 121; +// private static final int opc_ishr = 122; +// private static final int opc_lshr = 123; +// private static final int opc_iushr = 124; +// private static final int opc_lushr = 125; +// private static final int opc_iand = 126; +// private static final int opc_land = 127; +// private static final int opc_ior = 128; +// private static final int opc_lor = 129; +// private static final int opc_ixor = 130; +// private static final int opc_lxor = 131; +// private static final int opc_iinc = 132; +// private static final int opc_i2l = 133; +// private static final int opc_i2f = 134; +// private static final int opc_i2d = 135; +// private static final int opc_l2i = 136; +// private static final int opc_l2f = 137; +// private static final int opc_l2d = 138; +// private static final int opc_f2i = 139; +// private static final int opc_f2l = 140; +// private static final int opc_f2d = 141; +// private static final int opc_d2i = 142; +// private static final int opc_d2l = 143; +// private static final int opc_d2f = 144; +// private static final int opc_i2b = 145; +// private static final int opc_i2c = 146; +// private static final int opc_i2s = 147; +// private static final int opc_lcmp = 148; +// private static final int opc_fcmpl = 149; +// private static final int opc_fcmpg = 150; +// private static final int opc_dcmpl = 151; +// private static final int opc_dcmpg = 152; +// private static final int opc_ifeq = 153; +// private static final int opc_ifne = 154; +// private static final int opc_iflt = 155; +// private static final int opc_ifge = 156; +// private static final int opc_ifgt = 157; +// private static final int opc_ifle = 158; +// private static final int opc_if_icmpeq = 159; +// private static final int opc_if_icmpne = 160; +// private static final int opc_if_icmplt = 161; +// private static final int opc_if_icmpge = 162; +// private static final int opc_if_icmpgt = 163; +// private static final int opc_if_icmple = 164; +// private static final int opc_if_acmpeq = 165; +// private static final int opc_if_acmpne = 166; +// private static final int opc_goto = 167; +// private static final int opc_jsr = 168; +// private static final int opc_ret = 169; +// private static final int opc_tableswitch = 170; +// private static final int opc_lookupswitch = 171; + private static final int opc_ireturn = 172; + private static final int opc_lreturn = 173; + private static final int opc_freturn = 174; + private static final int opc_dreturn = 175; + private static final int opc_areturn = 176; + private static final int opc_return = 177; + private static final int opc_getstatic = 178; + private static final int opc_putstatic = 179; + private static final int opc_getfield = 180; +// private static final int opc_putfield = 181; + private static final int opc_invokevirtual = 182; + private static final int opc_invokespecial = 183; + private static final int opc_invokestatic = 184; + private static final int opc_invokeinterface = 185; + private static final int opc_new = 187; +// private static final int opc_newarray = 188; + private static final int opc_anewarray = 189; +// private static final int opc_arraylength = 190; + private static final int opc_athrow = 191; + private static final int opc_checkcast = 192; +// private static final int opc_instanceof = 193; +// private static final int opc_monitorenter = 194; +// private static final int opc_monitorexit = 195; + private static final int opc_wide = 196; +// private static final int opc_multianewarray = 197; +// private static final int opc_ifnull = 198; +// private static final int opc_ifnonnull = 199; +// private static final int opc_goto_w = 200; +// private static final int opc_jsr_w = 201; + + // end of constants copied from sun.tools.java.RuntimeConstants + + /** name of the superclass of proxy classes */ + private static final String superclassName = "java/lang/reflect/Proxy"; + + /** name of field for storing a proxy instance's invocation handler */ + private static final String handlerFieldName = "h"; + + /** debugging flag for saving generated class files */ + private static final boolean saveGeneratedFiles = + java.security.AccessController.doPrivileged( + new GetBooleanAction( + "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue(); + + /** + * Generate a public proxy class given a name and a list of proxy interfaces. + */ + static byte[] generateProxyClass(final String name, + List> interfaces) { + return generateProxyClass(name, interfaces, (ACC_PUBLIC | ACC_FINAL | ACC_SUPER)); + } + + /** + * Generate a proxy class given a name and a list of proxy interfaces. + * + * @param name the class name of the proxy class + * @param interfaces proxy interfaces + * @param accessFlags access flags of the proxy class + */ + static byte[] generateProxyClass(final String name, + List> interfaces, + int accessFlags) + { + ProxyGenerator_v49 gen = new ProxyGenerator_v49(name, interfaces, accessFlags); + final byte[] classFile = gen.generateClassFile(); + + if (saveGeneratedFiles) { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + try { + int i = name.lastIndexOf('.'); + Path path; + if (i > 0) { + Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar)); + Files.createDirectories(dir); + path = dir.resolve(name.substring(i+1, name.length()) + ".class"); + } else { + path = Path.of(name + ".class"); + } + Files.write(path, classFile); + return null; + } catch (IOException e) { + throw new InternalError( + "I/O exception saving generated file: " + e); + } + } + }); + } + + return classFile; + } + + /* preloaded Method objects for methods in java.lang.Object */ + private static Method hashCodeMethod; + private static Method equalsMethod; + private static Method toStringMethod; + static { + try { + hashCodeMethod = Object.class.getMethod("hashCode"); + equalsMethod = + Object.class.getMethod("equals", new Class[] { Object.class }); + toStringMethod = Object.class.getMethod("toString"); + } catch (NoSuchMethodException e) { + throw new NoSuchMethodError(e.getMessage()); + } + } + + /** name of proxy class */ + private String className; + + /** proxy interfaces */ + private List> interfaces; + + /** proxy class access flags */ + private int accessFlags; + + /** constant pool of class being generated */ + private ConstantPool cp = new ConstantPool(); + + /** FieldInfo struct for each field of generated class */ + private List fields = new ArrayList<>(); + + /** MethodInfo struct for each method of generated class */ + private List methods = new ArrayList<>(); + + /** + * maps method signature string to list of ProxyMethod objects for + * proxy methods with that signature + */ + private Map> proxyMethods = new LinkedHashMap<>(); + + /** count of ProxyMethod objects added to proxyMethods */ + private int proxyMethodCount = 0; + + /** + * Construct a ProxyGenerator to generate a proxy class with the + * specified name and for the given interfaces. + * + * A ProxyGenerator object contains the state for the ongoing + * generation of a particular proxy class. + */ + ProxyGenerator_v49(String className, List> interfaces, int accessFlags) { + this.className = className; + this.interfaces = interfaces; + this.accessFlags = accessFlags; + } + + /** + * Generate a class file for the proxy class. This method drives the + * class file generation process. + */ + private byte[] generateClassFile() { + + /* ============================================================ + * Step 1: Assemble ProxyMethod objects for all methods to + * generate proxy dispatching code for. + */ + + /* + * Record that proxy methods are needed for the hashCode, equals, + * and toString methods of java.lang.Object. This is done before + * the methods from the proxy interfaces so that the methods from + * java.lang.Object take precedence over duplicate methods in the + * proxy interfaces. + */ + addProxyMethod(hashCodeMethod, Object.class); + addProxyMethod(equalsMethod, Object.class); + addProxyMethod(toStringMethod, Object.class); + + /* + * Now record all of the methods from the proxy interfaces, giving + * earlier interfaces precedence over later ones with duplicate + * methods. + */ + for (Class intf : interfaces) { + for (Method m : intf.getMethods()) { + if (!Modifier.isStatic(m.getModifiers())) { + addProxyMethod(m, intf); + } + } + } + + /* + * For each set of proxy methods with the same signature, + * verify that the methods' return types are compatible. + */ + for (List sigmethods : proxyMethods.values()) { + checkReturnTypes(sigmethods); + } + + /* ============================================================ + * Step 2: Assemble FieldInfo and MethodInfo structs for all of + * fields and methods in the class we are generating. + */ + try { + methods.add(generateConstructor()); + + for (List sigmethods : proxyMethods.values()) { + for (ProxyMethod pm : sigmethods) { + + // add static field for method's Method object + fields.add(new FieldInfo(pm.methodFieldName, + "Ljava/lang/reflect/Method;", + ACC_PRIVATE | ACC_STATIC)); + + // generate code for proxy method and add it + methods.add(pm.generateMethod()); + } + } + + methods.add(generateStaticInitializer()); + + } catch (IOException e) { + throw new InternalError("unexpected I/O Exception", e); + } + + if (methods.size() > 65535) { + throw new IllegalArgumentException("method limit exceeded"); + } + if (fields.size() > 65535) { + throw new IllegalArgumentException("field limit exceeded"); + } + + /* ============================================================ + * Step 3: Write the final class file. + */ + + /* + * Make sure that constant pool indexes are reserved for the + * following items before starting to write the final class file. + */ + cp.getClass(dotToSlash(className)); + cp.getClass(superclassName); + for (Class intf: interfaces) { + cp.getClass(dotToSlash(intf.getName())); + } + + /* + * Disallow new constant pool additions beyond this point, since + * we are about to write the final constant pool table. + */ + cp.setReadOnly(); + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream dout = new DataOutputStream(bout); + + try { + /* + * Write all the items of the "ClassFile" structure. + * See JVMS section 4.1. + */ + // u4 magic; + dout.writeInt(0xCAFEBABE); + // u2 minor_version; + dout.writeShort(CLASSFILE_MINOR_VERSION); + // u2 major_version; + dout.writeShort(CLASSFILE_MAJOR_VERSION); + + cp.write(dout); // (write constant pool) + + // u2 access_flags; + dout.writeShort(accessFlags); + // u2 this_class; + dout.writeShort(cp.getClass(dotToSlash(className))); + // u2 super_class; + dout.writeShort(cp.getClass(superclassName)); + + // u2 interfaces_count; + dout.writeShort(interfaces.size()); + // u2 interfaces[interfaces_count]; + for (Class intf : interfaces) { + dout.writeShort(cp.getClass( + dotToSlash(intf.getName()))); + } + + // u2 fields_count; + dout.writeShort(fields.size()); + // field_info fields[fields_count]; + for (FieldInfo f : fields) { + f.write(dout); + } + + // u2 methods_count; + dout.writeShort(methods.size()); + // method_info methods[methods_count]; + for (MethodInfo m : methods) { + m.write(dout); + } + + // u2 attributes_count; + dout.writeShort(0); // (no ClassFile attributes for proxy classes) + + } catch (IOException e) { + throw new InternalError("unexpected I/O Exception", e); + } + + return bout.toByteArray(); + } + + /** + * Add another method to be proxied, either by creating a new + * ProxyMethod object or augmenting an old one for a duplicate + * method. + * + * "fromClass" indicates the proxy interface that the method was + * found through, which may be different from (a subinterface of) + * the method's "declaring class". Note that the first Method + * object passed for a given name and descriptor identifies the + * Method object (and thus the declaring class) that will be + * passed to the invocation handler's "invoke" method for a given + * set of duplicate methods. + */ + private void addProxyMethod(Method m, Class fromClass) { + String name = m.getName(); + Class[] parameterTypes = m.getParameterTypes(); + Class returnType = m.getReturnType(); + Class[] exceptionTypes = m.getExceptionTypes(); + + String sig = name + getParameterDescriptors(parameterTypes); + List sigmethods = proxyMethods.get(sig); + if (sigmethods != null) { + for (ProxyMethod pm : sigmethods) { + if (returnType == pm.returnType) { + /* + * Found a match: reduce exception types to the + * greatest set of exceptions that can thrown + * compatibly with the throws clauses of both + * overridden methods. + */ + List> legalExceptions = new ArrayList<>(); + collectCompatibleTypes( + exceptionTypes, pm.exceptionTypes, legalExceptions); + collectCompatibleTypes( + pm.exceptionTypes, exceptionTypes, legalExceptions); + pm.exceptionTypes = new Class[legalExceptions.size()]; + pm.exceptionTypes = + legalExceptions.toArray(pm.exceptionTypes); + return; + } + } + } else { + sigmethods = new ArrayList<>(3); + proxyMethods.put(sig, sigmethods); + } + sigmethods.add(new ProxyMethod(name, parameterTypes, returnType, + exceptionTypes, fromClass)); + } + + /** + * For a given set of proxy methods with the same signature, check + * that their return types are compatible according to the Proxy + * specification. + * + * Specifically, if there is more than one such method, then all + * of the return types must be reference types, and there must be + * one return type that is assignable to each of the rest of them. + */ + private static void checkReturnTypes(List methods) { + /* + * If there is only one method with a given signature, there + * cannot be a conflict. This is the only case in which a + * primitive (or void) return type is allowed. + */ + if (methods.size() < 2) { + return; + } + + /* + * List of return types that are not yet known to be + * assignable from ("covered" by) any of the others. + */ + LinkedList> uncoveredReturnTypes = new LinkedList<>(); + + nextNewReturnType: + for (ProxyMethod pm : methods) { + Class newReturnType = pm.returnType; + if (newReturnType.isPrimitive()) { + throw new IllegalArgumentException( + "methods with same signature " + + getFriendlyMethodSignature(pm.methodName, + pm.parameterTypes) + + " but incompatible return types: " + + newReturnType.getName() + " and others"); + } + boolean added = false; + + /* + * Compare the new return type to the existing uncovered + * return types. + */ + ListIterator> liter = uncoveredReturnTypes.listIterator(); + while (liter.hasNext()) { + Class uncoveredReturnType = liter.next(); + + /* + * If an existing uncovered return type is assignable + * to this new one, then we can forget the new one. + */ + if (newReturnType.isAssignableFrom(uncoveredReturnType)) { + assert !added; + continue nextNewReturnType; + } + + /* + * If the new return type is assignable to an existing + * uncovered one, then should replace the existing one + * with the new one (or just forget the existing one, + * if the new one has already be put in the list). + */ + if (uncoveredReturnType.isAssignableFrom(newReturnType)) { + // (we can assume that each return type is unique) + if (!added) { + liter.set(newReturnType); + added = true; + } else { + liter.remove(); + } + } + } + + /* + * If we got through the list of existing uncovered return + * types without an assignability relationship, then add + * the new return type to the list of uncovered ones. + */ + if (!added) { + uncoveredReturnTypes.add(newReturnType); + } + } + + /* + * We shouldn't end up with more than one return type that is + * not assignable from any of the others. + */ + if (uncoveredReturnTypes.size() > 1) { + ProxyMethod pm = methods.get(0); + throw new IllegalArgumentException( + "methods with same signature " + + getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) + + " but incompatible return types: " + uncoveredReturnTypes); + } + } + + /** + * A FieldInfo object contains information about a particular field + * in the class being generated. The class mirrors the data items of + * the "field_info" structure of the class file format (see JVMS 4.5). + */ + private class FieldInfo { + public int accessFlags; + public String name; + public String descriptor; + + public FieldInfo(String name, String descriptor, int accessFlags) { + this.name = name; + this.descriptor = descriptor; + this.accessFlags = accessFlags; + + /* + * Make sure that constant pool indexes are reserved for the + * following items before starting to write the final class file. + */ + cp.getUtf8(name); + cp.getUtf8(descriptor); + } + + public void write(DataOutputStream out) throws IOException { + /* + * Write all the items of the "field_info" structure. + * See JVMS section 4.5. + */ + // u2 access_flags; + out.writeShort(accessFlags); + // u2 name_index; + out.writeShort(cp.getUtf8(name)); + // u2 descriptor_index; + out.writeShort(cp.getUtf8(descriptor)); + // u2 attributes_count; + out.writeShort(0); // (no field_info attributes for proxy classes) + } + } + + /** + * An ExceptionTableEntry object holds values for the data items of + * an entry in the "exception_table" item of the "Code" attribute of + * "method_info" structures (see JVMS 4.7.3). + */ + private static class ExceptionTableEntry { + public short startPc; + public short endPc; + public short handlerPc; + public short catchType; + + public ExceptionTableEntry(short startPc, short endPc, + short handlerPc, short catchType) + { + this.startPc = startPc; + this.endPc = endPc; + this.handlerPc = handlerPc; + this.catchType = catchType; + } + }; + + /** + * A MethodInfo object contains information about a particular method + * in the class being generated. This class mirrors the data items of + * the "method_info" structure of the class file format (see JVMS 4.6). + */ + private class MethodInfo { + public int accessFlags; + public String name; + public String descriptor; + public short maxStack; + public short maxLocals; + public ByteArrayOutputStream code = new ByteArrayOutputStream(); + public List exceptionTable = + new ArrayList(); + public short[] declaredExceptions; + + public MethodInfo(String name, String descriptor, int accessFlags) { + this.name = name; + this.descriptor = descriptor; + this.accessFlags = accessFlags; + + /* + * Make sure that constant pool indexes are reserved for the + * following items before starting to write the final class file. + */ + cp.getUtf8(name); + cp.getUtf8(descriptor); + cp.getUtf8("Code"); + cp.getUtf8("Exceptions"); + } + + public void write(DataOutputStream out) throws IOException { + /* + * Write all the items of the "method_info" structure. + * See JVMS section 4.6. + */ + // u2 access_flags; + out.writeShort(accessFlags); + // u2 name_index; + out.writeShort(cp.getUtf8(name)); + // u2 descriptor_index; + out.writeShort(cp.getUtf8(descriptor)); + // u2 attributes_count; + out.writeShort(2); // (two method_info attributes:) + + // Write "Code" attribute. See JVMS section 4.7.3. + + // u2 attribute_name_index; + out.writeShort(cp.getUtf8("Code")); + // u4 attribute_length; + out.writeInt(12 + code.size() + 8 * exceptionTable.size()); + // u2 max_stack; + out.writeShort(maxStack); + // u2 max_locals; + out.writeShort(maxLocals); + // u2 code_length; + out.writeInt(code.size()); + // u1 code[code_length]; + code.writeTo(out); + // u2 exception_table_length; + out.writeShort(exceptionTable.size()); + for (ExceptionTableEntry e : exceptionTable) { + // u2 start_pc; + out.writeShort(e.startPc); + // u2 end_pc; + out.writeShort(e.endPc); + // u2 handler_pc; + out.writeShort(e.handlerPc); + // u2 catch_type; + out.writeShort(e.catchType); + } + // u2 attributes_count; + out.writeShort(0); + + // write "Exceptions" attribute. See JVMS section 4.7.4. + + // u2 attribute_name_index; + out.writeShort(cp.getUtf8("Exceptions")); + // u4 attributes_length; + out.writeInt(2 + 2 * declaredExceptions.length); + // u2 number_of_exceptions; + out.writeShort(declaredExceptions.length); + // u2 exception_index_table[number_of_exceptions]; + for (short value : declaredExceptions) { + out.writeShort(value); + } + } + + } + + /** + * A ProxyMethod object represents a proxy method in the proxy class + * being generated: a method whose implementation will encode and + * dispatch invocations to the proxy instance's invocation handler. + */ + private class ProxyMethod { + + public String methodName; + public Class[] parameterTypes; + public Class returnType; + public Class[] exceptionTypes; + public Class fromClass; + public String methodFieldName; + + private ProxyMethod(String methodName, Class[] parameterTypes, + Class returnType, Class[] exceptionTypes, + Class fromClass) + { + this.methodName = methodName; + this.parameterTypes = parameterTypes; + this.returnType = returnType; + this.exceptionTypes = exceptionTypes; + this.fromClass = fromClass; + this.methodFieldName = "m" + proxyMethodCount++; + } + + /** + * Return a MethodInfo object for this method, including generating + * the code and exception table entry. + */ + private MethodInfo generateMethod() throws IOException { + String desc = getMethodDescriptor(parameterTypes, returnType); + MethodInfo minfo = new MethodInfo(methodName, desc, + ACC_PUBLIC | ACC_FINAL); + + int[] parameterSlot = new int[parameterTypes.length]; + int nextSlot = 1; + for (int i = 0; i < parameterSlot.length; i++) { + parameterSlot[i] = nextSlot; + nextSlot += getWordsPerType(parameterTypes[i]); + } + int localSlot0 = nextSlot; + short pc, tryBegin = 0, tryEnd; + + DataOutputStream out = new DataOutputStream(minfo.code); + + code_aload(0, out); + + out.writeByte(opc_getfield); + out.writeShort(cp.getFieldRef( + superclassName, + handlerFieldName, "Ljava/lang/reflect/InvocationHandler;")); + + code_aload(0, out); + + out.writeByte(opc_getstatic); + out.writeShort(cp.getFieldRef( + dotToSlash(className), + methodFieldName, "Ljava/lang/reflect/Method;")); + + if (parameterTypes.length > 0) { + + code_ipush(parameterTypes.length, out); + + out.writeByte(opc_anewarray); + out.writeShort(cp.getClass("java/lang/Object")); + + for (int i = 0; i < parameterTypes.length; i++) { + + out.writeByte(opc_dup); + + code_ipush(i, out); + + codeWrapArgument(parameterTypes[i], parameterSlot[i], out); + + out.writeByte(opc_aastore); + } + } else { + + out.writeByte(opc_aconst_null); + } + + out.writeByte(opc_invokeinterface); + out.writeShort(cp.getInterfaceMethodRef( + "java/lang/reflect/InvocationHandler", + "invoke", + "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + + "[Ljava/lang/Object;)Ljava/lang/Object;")); + out.writeByte(4); + out.writeByte(0); + + if (returnType == void.class) { + + out.writeByte(opc_pop); + + out.writeByte(opc_return); + + } else { + + codeUnwrapReturnValue(returnType, out); + } + + tryEnd = pc = (short) minfo.code.size(); + + List> catchList = computeUniqueCatchList(exceptionTypes); + if (catchList.size() > 0) { + + for (Class ex : catchList) { + minfo.exceptionTable.add(new ExceptionTableEntry( + tryBegin, tryEnd, pc, + cp.getClass(dotToSlash(ex.getName())))); + } + + out.writeByte(opc_athrow); + + pc = (short) minfo.code.size(); + + minfo.exceptionTable.add(new ExceptionTableEntry( + tryBegin, tryEnd, pc, cp.getClass("java/lang/Throwable"))); + + code_astore(localSlot0, out); + + out.writeByte(opc_new); + out.writeShort(cp.getClass( + "java/lang/reflect/UndeclaredThrowableException")); + + out.writeByte(opc_dup); + + code_aload(localSlot0, out); + + out.writeByte(opc_invokespecial); + + out.writeShort(cp.getMethodRef( + "java/lang/reflect/UndeclaredThrowableException", + "", "(Ljava/lang/Throwable;)V")); + + out.writeByte(opc_athrow); + } + + if (minfo.code.size() > 65535) { + throw new IllegalArgumentException("code size limit exceeded"); + } + + minfo.maxStack = 10; + minfo.maxLocals = (short) (localSlot0 + 1); + minfo.declaredExceptions = new short[exceptionTypes.length]; + for (int i = 0; i < exceptionTypes.length; i++) { + minfo.declaredExceptions[i] = cp.getClass( + dotToSlash(exceptionTypes[i].getName())); + } + + return minfo; + } + + /** + * Generate code for wrapping an argument of the given type + * whose value can be found at the specified local variable + * index, in order for it to be passed (as an Object) to the + * invocation handler's "invoke" method. The code is written + * to the supplied stream. + */ + private void codeWrapArgument(Class type, int slot, + DataOutputStream out) + throws IOException + { + if (type.isPrimitive()) { + PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); + + if (type == int.class || + type == boolean.class || + type == byte.class || + type == char.class || + type == short.class) + { + code_iload(slot, out); + } else if (type == long.class) { + code_lload(slot, out); + } else if (type == float.class) { + code_fload(slot, out); + } else if (type == double.class) { + code_dload(slot, out); + } else { + throw new AssertionError(); + } + + out.writeByte(opc_invokestatic); + out.writeShort(cp.getMethodRef( + prim.wrapperClassName, + "valueOf", prim.wrapperValueOfDesc)); + + } else { + + code_aload(slot, out); + } + } + + /** + * Generate code for unwrapping a return value of the given + * type from the invocation handler's "invoke" method (as type + * Object) to its correct type. The code is written to the + * supplied stream. + */ + private void codeUnwrapReturnValue(Class type, DataOutputStream out) + throws IOException + { + if (type.isPrimitive()) { + PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); + + out.writeByte(opc_checkcast); + out.writeShort(cp.getClass(prim.wrapperClassName)); + + out.writeByte(opc_invokevirtual); + out.writeShort(cp.getMethodRef( + prim.wrapperClassName, + prim.unwrapMethodName, prim.unwrapMethodDesc)); + + if (type == int.class || + type == boolean.class || + type == byte.class || + type == char.class || + type == short.class) + { + out.writeByte(opc_ireturn); + } else if (type == long.class) { + out.writeByte(opc_lreturn); + } else if (type == float.class) { + out.writeByte(opc_freturn); + } else if (type == double.class) { + out.writeByte(opc_dreturn); + } else { + throw new AssertionError(); + } + + } else { + + out.writeByte(opc_checkcast); + out.writeShort(cp.getClass(dotToSlash(type.getName()))); + + out.writeByte(opc_areturn); + } + } + + /** + * Generate code for initializing the static field that stores + * the Method object for this proxy method. The code is written + * to the supplied stream. + */ + private void codeFieldInitialization(DataOutputStream out) + throws IOException + { + codeClassForName(fromClass, out); + + code_ldc(cp.getString(methodName), out); + + code_ipush(parameterTypes.length, out); + + out.writeByte(opc_anewarray); + out.writeShort(cp.getClass("java/lang/Class")); + + for (int i = 0; i < parameterTypes.length; i++) { + + out.writeByte(opc_dup); + + code_ipush(i, out); + + if (parameterTypes[i].isPrimitive()) { + PrimitiveTypeInfo prim = + PrimitiveTypeInfo.get(parameterTypes[i]); + + out.writeByte(opc_getstatic); + out.writeShort(cp.getFieldRef( + prim.wrapperClassName, "TYPE", "Ljava/lang/Class;")); + + } else { + codeClassForName(parameterTypes[i], out); + } + + out.writeByte(opc_aastore); + } + + out.writeByte(opc_invokevirtual); + out.writeShort(cp.getMethodRef( + "java/lang/Class", + "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)" + + "Ljava/lang/reflect/Method;")); + + out.writeByte(opc_putstatic); + out.writeShort(cp.getFieldRef( + dotToSlash(className), + methodFieldName, "Ljava/lang/reflect/Method;")); + } + } + + /** + * Generate the constructor method for the proxy class. + */ + private MethodInfo generateConstructor() throws IOException { + MethodInfo minfo = new MethodInfo( + "", "(Ljava/lang/reflect/InvocationHandler;)V", + ACC_PUBLIC); + + DataOutputStream out = new DataOutputStream(minfo.code); + + code_aload(0, out); + + code_aload(1, out); + + out.writeByte(opc_invokespecial); + out.writeShort(cp.getMethodRef( + superclassName, + "", "(Ljava/lang/reflect/InvocationHandler;)V")); + + out.writeByte(opc_return); + + minfo.maxStack = 10; + minfo.maxLocals = 2; + minfo.declaredExceptions = new short[0]; + + return minfo; + } + + /** + * Generate the static initializer method for the proxy class. + */ + private MethodInfo generateStaticInitializer() throws IOException { + MethodInfo minfo = new MethodInfo( + "", "()V", ACC_STATIC); + + int localSlot0 = 1; + short pc, tryBegin = 0, tryEnd; + + DataOutputStream out = new DataOutputStream(minfo.code); + + for (List sigmethods : proxyMethods.values()) { + for (ProxyMethod pm : sigmethods) { + pm.codeFieldInitialization(out); + } + } + + out.writeByte(opc_return); + + tryEnd = pc = (short) minfo.code.size(); + + minfo.exceptionTable.add(new ExceptionTableEntry( + tryBegin, tryEnd, pc, + cp.getClass("java/lang/NoSuchMethodException"))); + + code_astore(localSlot0, out); + + out.writeByte(opc_new); + out.writeShort(cp.getClass("java/lang/NoSuchMethodError")); + + out.writeByte(opc_dup); + + code_aload(localSlot0, out); + + out.writeByte(opc_invokevirtual); + out.writeShort(cp.getMethodRef( + "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); + + out.writeByte(opc_invokespecial); + out.writeShort(cp.getMethodRef( + "java/lang/NoSuchMethodError", "", "(Ljava/lang/String;)V")); + + out.writeByte(opc_athrow); + + pc = (short) minfo.code.size(); + + minfo.exceptionTable.add(new ExceptionTableEntry( + tryBegin, tryEnd, pc, + cp.getClass("java/lang/ClassNotFoundException"))); + + code_astore(localSlot0, out); + + out.writeByte(opc_new); + out.writeShort(cp.getClass("java/lang/NoClassDefFoundError")); + + out.writeByte(opc_dup); + + code_aload(localSlot0, out); + + out.writeByte(opc_invokevirtual); + out.writeShort(cp.getMethodRef( + "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); + + out.writeByte(opc_invokespecial); + out.writeShort(cp.getMethodRef( + "java/lang/NoClassDefFoundError", + "", "(Ljava/lang/String;)V")); + + out.writeByte(opc_athrow); + + if (minfo.code.size() > 65535) { + throw new IllegalArgumentException("code size limit exceeded"); + } + + minfo.maxStack = 10; + minfo.maxLocals = (short) (localSlot0 + 1); + minfo.declaredExceptions = new short[0]; + + return minfo; + } + + + /* + * =============== Code Generation Utility Methods =============== + */ + + /* + * The following methods generate code for the load or store operation + * indicated by their name for the given local variable. The code is + * written to the supplied stream. + */ + + private void code_iload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_iload, opc_iload_0, out); + } + + private void code_lload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_lload, opc_lload_0, out); + } + + private void code_fload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_fload, opc_fload_0, out); + } + + private void code_dload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_dload, opc_dload_0, out); + } + + private void code_aload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_aload, opc_aload_0, out); + } + +// private void code_istore(int lvar, DataOutputStream out) +// throws IOException +// { +// codeLocalLoadStore(lvar, opc_istore, opc_istore_0, out); +// } + +// private void code_lstore(int lvar, DataOutputStream out) +// throws IOException +// { +// codeLocalLoadStore(lvar, opc_lstore, opc_lstore_0, out); +// } + +// private void code_fstore(int lvar, DataOutputStream out) +// throws IOException +// { +// codeLocalLoadStore(lvar, opc_fstore, opc_fstore_0, out); +// } + +// private void code_dstore(int lvar, DataOutputStream out) +// throws IOException +// { +// codeLocalLoadStore(lvar, opc_dstore, opc_dstore_0, out); +// } + + private void code_astore(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_astore, opc_astore_0, out); + } + + /** + * Generate code for a load or store instruction for the given local + * variable. The code is written to the supplied stream. + * + * "opcode" indicates the opcode form of the desired load or store + * instruction that takes an explicit local variable index, and + * "opcode_0" indicates the corresponding form of the instruction + * with the implicit index 0. + */ + private void codeLocalLoadStore(int lvar, int opcode, int opcode_0, + DataOutputStream out) + throws IOException + { + assert lvar >= 0 && lvar <= 0xFFFF; + if (lvar <= 3) { + out.writeByte(opcode_0 + lvar); + } else if (lvar <= 0xFF) { + out.writeByte(opcode); + out.writeByte(lvar & 0xFF); + } else { + /* + * Use the "wide" instruction modifier for local variable + * indexes that do not fit into an unsigned byte. + */ + out.writeByte(opc_wide); + out.writeByte(opcode); + out.writeShort(lvar & 0xFFFF); + } + } + + /** + * Generate code for an "ldc" instruction for the given constant pool + * index (the "ldc_w" instruction is used if the index does not fit + * into an unsigned byte). The code is written to the supplied stream. + */ + private void code_ldc(int index, DataOutputStream out) + throws IOException + { + assert index >= 0 && index <= 0xFFFF; + if (index <= 0xFF) { + out.writeByte(opc_ldc); + out.writeByte(index & 0xFF); + } else { + out.writeByte(opc_ldc_w); + out.writeShort(index & 0xFFFF); + } + } + + /** + * Generate code to push a constant integer value on to the operand + * stack, using the "iconst_", "bipush", or "sipush" instructions + * depending on the size of the value. The code is written to the + * supplied stream. + */ + private void code_ipush(int value, DataOutputStream out) + throws IOException + { + if (value >= -1 && value <= 5) { + out.writeByte(opc_iconst_0 + value); + } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { + out.writeByte(opc_bipush); + out.writeByte(value & 0xFF); + } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { + out.writeByte(opc_sipush); + out.writeShort(value & 0xFFFF); + } else { + throw new AssertionError(); + } + } + + /** + * Generate code to invoke the Class.forName with the name of the given + * class to get its Class object at runtime. The code is written to + * the supplied stream. Note that the code generated by this method + * may cause the checked ClassNotFoundException to be thrown. + */ + private void codeClassForName(Class cl, DataOutputStream out) + throws IOException + { + code_ldc(cp.getString(cl.getName()), out); + + out.writeByte(opc_invokestatic); + out.writeShort(cp.getMethodRef( + "java/lang/Class", + "forName", "(Ljava/lang/String;)Ljava/lang/Class;")); + } + + + /* + * ==================== General Utility Methods ==================== + */ + + /** + * 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 4.2). + */ + private static String dotToSlash(String name) { + return name.replace('.', '/'); + } + + /** + * Return the "method descriptor" string for a method with the given + * parameter types and return type. See JVMS section 4.3.3. + */ + private static String getMethodDescriptor(Class[] parameterTypes, + Class returnType) + { + return getParameterDescriptors(parameterTypes) + + ((returnType == void.class) ? "V" : getFieldType(returnType)); + } + + /** + * Return the list of "parameter descriptor" strings enclosed in + * parentheses corresponding to the given parameter types (in other + * words, a method descriptor without a return descriptor). This + * string is useful for constructing string keys for methods without + * regard to their return type. + */ + private static String getParameterDescriptors(Class[] parameterTypes) { + StringBuilder desc = new StringBuilder("("); + for (int i = 0; i < parameterTypes.length; i++) { + desc.append(getFieldType(parameterTypes[i])); + } + desc.append(')'); + return desc.toString(); + } + + /** + * Return the "field type" string for the given type, appropriate for + * a field descriptor, a parameter descriptor, or a return descriptor + * other than "void". See JVMS section 4.3.2. + */ + private static String getFieldType(Class type) { + if (type.isPrimitive()) { + return PrimitiveTypeInfo.get(type).baseTypeString; + } else if (type.isArray()) { + /* + * According to JLS 20.3.2, the getName() method on Class does + * return the VM type descriptor format for array classes (only); + * using that should be quicker than the otherwise obvious code: + * + * return "[" + getTypeDescriptor(type.getComponentType()); + */ + return type.getName().replace('.', '/'); + } else { + return "L" + dotToSlash(type.getName()) + ";"; + } + } + + /** + * Returns a human-readable string representing the signature of a + * method with the given name and parameter types. + */ + private static String getFriendlyMethodSignature(String name, + Class[] parameterTypes) + { + StringBuilder sig = new StringBuilder(name); + sig.append('('); + for (int i = 0; i < parameterTypes.length; i++) { + if (i > 0) { + sig.append(','); + } + Class parameterType = parameterTypes[i]; + int dimensions = 0; + while (parameterType.isArray()) { + parameterType = parameterType.getComponentType(); + dimensions++; + } + sig.append(parameterType.getName()); + while (dimensions-- > 0) { + sig.append("[]"); + } + } + sig.append(')'); + return sig.toString(); + } + + /** + * Return the number of abstract "words", or consecutive local variable + * indexes, required to contain a value of the given type. See JVMS + * section 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 + * least one of the types in the "with" array. + * + * This method is useful for computing the greatest common set of + * declared exceptions from duplicate methods inherited from + * different interfaces. + */ + private static void collectCompatibleTypes(Class[] from, + Class[] with, + List> list) + { + for (Class fc: from) { + if (!list.contains(fc)) { + for (Class wc: with) { + if (wc.isAssignableFrom(fc)) { + list.add(fc); + break; + } + } + } + } + } + + /** + * Given the exceptions declared in the throws clause of a proxy method, + * compute the exceptions that need to be caught from the invocation + * handler's invoke method and rethrown intact in the method's + * implementation before catching other Throwables and wrapping them + * in UndeclaredThrowableExceptions. + * + * The exceptions to be caught are returned in a List object. Each + * exception in the returned list is guaranteed to not be a subclass of + * any of the other exceptions in the list, so the catch blocks for + * these exceptions may be generated in any order relative to each other. + * + * Error and RuntimeException are each always contained by the returned + * list (if none of their superclasses are contained), since those + * unchecked exceptions should always be rethrown intact, and thus their + * subclasses will never appear in the returned list. + * + * The returned List will be empty if java.lang.Throwable is in the + * given list of declared exceptions, indicating that no exceptions + * need to be caught. + */ + private static List> computeUniqueCatchList(Class[] exceptions) { + List> uniqueList = new ArrayList<>(); + // unique exceptions to catch + + uniqueList.add(Error.class); // always catch/rethrow these + uniqueList.add(RuntimeException.class); + + nextException: + for (Class ex: exceptions) { + if (ex.isAssignableFrom(Throwable.class)) { + /* + * If Throwable is declared to be thrown by the proxy method, + * then no catch blocks are necessary, because the invoke + * can, at most, throw Throwable anyway. + */ + uniqueList.clear(); + break; + } else if (!Throwable.class.isAssignableFrom(ex)) { + /* + * Ignore types that cannot be thrown by the invoke method. + */ + continue; + } + /* + * Compare this exception against the current list of + * exceptions that need to be caught: + */ + for (int j = 0; j < uniqueList.size();) { + Class ex2 = uniqueList.get(j); + if (ex2.isAssignableFrom(ex)) { + /* + * if a superclass of this exception is already on + * the list to catch, then ignore this one and continue; + */ + continue nextException; + } else if (ex.isAssignableFrom(ex2)) { + /* + * if a subclass of this exception is on the list + * to catch, then remove it; + */ + uniqueList.remove(j); + } else { + j++; // else continue comparing. + } + } + // This exception is unique (so far): add it to the list to catch. + uniqueList.add(ex); + } + return uniqueList; + } + + /** + * A PrimitiveTypeInfo object contains assorted information about + * a primitive type in its public fields. The struct for a particular + * primitive type can be obtained using the static "get" method. + */ + private static class PrimitiveTypeInfo { + + /** "base type" used in various descriptors (see JVMS section 4.3.2) */ + public String baseTypeString; + + /** name of corresponding wrapper class */ + public String wrapperClassName; + + /** method descriptor for wrapper class "valueOf" factory method */ + public String wrapperValueOfDesc; + + /** name of wrapper class method for retrieving primitive value */ + public String unwrapMethodName; + + /** descriptor of same method */ + public String unwrapMethodDesc; + + private static Map,PrimitiveTypeInfo> table = new HashMap<>(); + static { + add(byte.class, Byte.class); + add(char.class, Character.class); + add(double.class, Double.class); + add(float.class, Float.class); + add(int.class, Integer.class); + add(long.class, Long.class); + add(short.class, Short.class); + add(boolean.class, Boolean.class); + } + + private static void add(Class primitiveClass, Class wrapperClass) { + table.put(primitiveClass, + new PrimitiveTypeInfo(primitiveClass, wrapperClass)); + } + + private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) { + assert primitiveClass.isPrimitive(); + + baseTypeString = + Array.newInstance(primitiveClass, 0) + .getClass().getName().substring(1); + wrapperClassName = dotToSlash(wrapperClass.getName()); + wrapperValueOfDesc = + "(" + baseTypeString + ")L" + wrapperClassName + ";"; + unwrapMethodName = primitiveClass.getName() + "Value"; + unwrapMethodDesc = "()" + baseTypeString; + } + + public static PrimitiveTypeInfo get(Class cl) { + return table.get(cl); + } + } + + + /** + * A ConstantPool object represents the constant pool of a class file + * being generated. This representation of a constant pool is designed + * specifically for use by ProxyGenerator; in particular, it assumes + * that constant pool entries will not need to be resorted (for example, + * by their type, as the Java compiler does), so that the final index + * value can be assigned and used when an entry is first created. + * + * Note that new entries cannot be created after the constant pool has + * been written to a class file. To prevent such logic errors, a + * ConstantPool instance can be marked "read only", so that further + * attempts to add new entries will fail with a runtime exception. + * + * See JVMS section 4.4 for more information about the constant pool + * of a class file. + */ + private static class ConstantPool { + + /** + * list of constant pool entries, in constant pool index order. + * + * This list is used when writing the constant pool to a stream + * and for assigning the next index value. Note that element 0 + * of this list corresponds to constant pool index 1. + */ + private List pool = new ArrayList<>(32); + + /** + * maps constant pool data of all types to constant pool indexes. + * + * This map is used to look up the index of an existing entry for + * values of all types. + */ + private Map map = new HashMap<>(16); + + /** true if no new constant pool entries may be added */ + private boolean readOnly = false; + + /** + * Get or assign the index for a CONSTANT_Utf8 entry. + */ + public short getUtf8(String s) { + if (s == null) { + throw new NullPointerException(); + } + return getValue(s); + } + + /** + * Get or assign the index for a CONSTANT_Integer entry. + */ + public short getInteger(int i) { + return getValue(i); + } + + /** + * Get or assign the index for a CONSTANT_Float entry. + */ + public short getFloat(float f) { + return getValue(f); + } + + /** + * Get or assign the index for a CONSTANT_Class entry. + */ + public short getClass(String name) { + short utf8Index = getUtf8(name); + return getIndirect(new IndirectEntry( + CONSTANT_CLASS, utf8Index)); + } + + /** + * Get or assign the index for a CONSTANT_String entry. + */ + public short getString(String s) { + short utf8Index = getUtf8(s); + return getIndirect(new IndirectEntry( + CONSTANT_STRING, utf8Index)); + } + + /** + * Get or assign the index for a CONSTANT_FieldRef entry. + */ + public short getFieldRef(String className, + String name, String descriptor) + { + short classIndex = getClass(className); + short nameAndTypeIndex = getNameAndType(name, descriptor); + return getIndirect(new IndirectEntry( + CONSTANT_FIELD, classIndex, nameAndTypeIndex)); + } + + /** + * Get or assign the index for a CONSTANT_MethodRef entry. + */ + public short getMethodRef(String className, + String name, String descriptor) + { + short classIndex = getClass(className); + short nameAndTypeIndex = getNameAndType(name, descriptor); + return getIndirect(new IndirectEntry( + CONSTANT_METHOD, classIndex, nameAndTypeIndex)); + } + + /** + * Get or assign the index for a CONSTANT_InterfaceMethodRef entry. + */ + public short getInterfaceMethodRef(String className, String name, + String descriptor) + { + short classIndex = getClass(className); + short nameAndTypeIndex = getNameAndType(name, descriptor); + return getIndirect(new IndirectEntry( + CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex)); + } + + /** + * Get or assign the index for a CONSTANT_NameAndType entry. + */ + public short getNameAndType(String name, String descriptor) { + short nameIndex = getUtf8(name); + short descriptorIndex = getUtf8(descriptor); + return getIndirect(new IndirectEntry( + CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex)); + } + + /** + * Set this ConstantPool instance to be "read only". + * + * After this method has been called, further requests to get + * an index for a non-existent entry will cause an InternalError + * to be thrown instead of creating of the entry. + */ + public void setReadOnly() { + readOnly = true; + } + + /** + * Write this constant pool to a stream as part of + * the class file format. + * + * This consists of writing the "constant_pool_count" and + * "constant_pool[]" items of the "ClassFile" structure, as + * described in JVMS section 4.1. + */ + public void write(OutputStream out) throws IOException { + DataOutputStream dataOut = new DataOutputStream(out); + + // constant_pool_count: number of entries plus one + dataOut.writeShort(pool.size() + 1); + + for (Entry e : pool) { + e.write(dataOut); + } + } + + /** + * Add a new constant pool entry and return its index. + */ + private short addEntry(Entry entry) { + pool.add(entry); + /* + * Note that this way of determining the index of the + * added entry is wrong if this pool supports + * CONSTANT_Long or CONSTANT_Double entries. + */ + if (pool.size() >= 65535) { + throw new IllegalArgumentException( + "constant pool size limit exceeded"); + } + return (short) pool.size(); + } + + /** + * Get or assign the index for an entry of a type that contains + * a direct value. The type of the given object determines the + * type of the desired entry as follows: + * + * java.lang.String CONSTANT_Utf8 + * java.lang.Integer CONSTANT_Integer + * java.lang.Float CONSTANT_Float + * java.lang.Long CONSTANT_Long + * java.lang.Double CONSTANT_DOUBLE + */ + private short getValue(Object key) { + Integer index = map.get(key); + if (index != null) { + return index.shortValue(); + } else { + if (readOnly) { + throw new InternalError( + "late constant pool addition: " + key); + } + short i = addEntry(new ValueEntry(key)); + map.put(key, (int)i); + return i; + } + } + + /** + * Get or assign the index for an entry of a type that contains + * references to other constant pool entries. + */ + private short getIndirect(IndirectEntry e) { + Integer index = map.get(e); + if (index != null) { + return index.shortValue(); + } else { + if (readOnly) { + throw new InternalError("late constant pool addition"); + } + short i = addEntry(e); + map.put(e, (int)i); + return i; + } + } + + /** + * Entry is the abstact superclass of all constant pool entry types + * that can be stored in the "pool" list; its purpose is to define a + * common method for writing constant pool entries to a class file. + */ + private abstract static class Entry { + public abstract void write(DataOutputStream out) + throws IOException; + } + + /** + * ValueEntry represents a constant pool entry of a type that + * contains a direct value (see the comments for the "getValue" + * method for a list of such types). + * + * ValueEntry objects are not used as keys for their entries in the + * Map "map", so no useful hashCode or equals methods are defined. + */ + private static class ValueEntry extends Entry { + private Object value; + + public ValueEntry(Object value) { + this.value = value; + } + + public void write(DataOutputStream out) throws IOException { + if (value instanceof String) { + out.writeByte(CONSTANT_UTF8); + out.writeUTF((String) value); + } else if (value instanceof Integer) { + out.writeByte(CONSTANT_INTEGER); + out.writeInt(((Integer) value).intValue()); + } else if (value instanceof Float) { + out.writeByte(CONSTANT_FLOAT); + out.writeFloat(((Float) value).floatValue()); + } else if (value instanceof Long) { + out.writeByte(CONSTANT_LONG); + out.writeLong(((Long) value).longValue()); + } else if (value instanceof Double) { + out.writeDouble(CONSTANT_DOUBLE); + out.writeDouble(((Double) value).doubleValue()); + } else { + throw new InternalError("bogus value entry: " + value); + } + } + } + + /** + * IndirectEntry represents a constant pool entry of a type that + * references other constant pool entries, i.e., the following types: + * + * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref, + * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and + * CONSTANT_NameAndType. + * + * Each of these entry types contains either one or two indexes of + * other constant pool entries. + * + * IndirectEntry objects are used as the keys for their entries in + * the Map "map", so the hashCode and equals methods are overridden + * to allow matching. + */ + private static class IndirectEntry extends Entry { + private int tag; + private short index0; + private short index1; + + /** + * Construct an IndirectEntry for a constant pool entry type + * that contains one index of another entry. + */ + public IndirectEntry(int tag, short index) { + this.tag = tag; + this.index0 = index; + this.index1 = 0; + } + + /** + * Construct an IndirectEntry for a constant pool entry type + * that contains two indexes for other entries. + */ + public IndirectEntry(int tag, short index0, short index1) { + this.tag = tag; + this.index0 = index0; + this.index1 = index1; + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeShort(index0); + /* + * If this entry type contains two indexes, write + * out the second, too. + */ + if (tag == CONSTANT_FIELD || + tag == CONSTANT_METHOD || + tag == CONSTANT_INTERFACEMETHOD || + tag == CONSTANT_NAMEANDTYPE) + { + out.writeShort(index1); + } + } + + public int hashCode() { + return tag + index0 + index1; + } + + public boolean equals(Object obj) { + if (obj instanceof IndirectEntry) { + IndirectEntry other = (IndirectEntry) obj; + if (tag == other.tag && + index0 == other.index0 && index1 == other.index1) + { + return true; + } + } + return false; + } + } + } +} --- /dev/null 2019-08-16 15:28:57.192077812 -0400 +++ new/test/jdk/java/lang/reflect/Proxy/ProxyGeneratorCombo.java 2019-08-21 16:28:43.592034130 -0400 @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Proxy Generator Combo tests + * @library /test/langtools/tools/javac/lib . + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.code + * jdk.compiler/com.sun.tools.javac.comp + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.tree + * jdk.compiler/com.sun.tools.javac.util + * @build combo.ComboTestHelper + * @run main/othervm ProxyGeneratorCombo + * @run main/othervm -Djdk.proxy.ProxyGenerator.v49=true ProxyGeneratorCombo + */ + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringJoiner; + +import combo.ComboInstance; +import combo.ComboParameter; +import combo.ComboTask.Result; +import combo.ComboTestHelper; + +public class ProxyGeneratorCombo extends ComboInstance { + + // The unique number to qualify interface names, unique across multiple runs + private static int uniqueId = 0; + + /** + * Class Access kinds. + */ + enum ClassAccessKind implements ComboParameter { + PUBLIC("public"), + PACKAGE(""), + ; + + String classAccessTemplate; + + ClassAccessKind(String classAccessTemplate) { + this.classAccessTemplate = classAccessTemplate; + } + + @Override + public String expand(String optParameter) { + return classAccessTemplate; + } + } + + /** + * Signatures of methods to be tested. + */ + enum MethodsKind implements ComboParameter { + NONE(""), + ZERO("#{METHODACCESS} void zero() #{EXCEPTION};"), + ONE("#{METHODACCESS} void one(#{ARG[0]} a) #{EXCEPTION};"), + TWO("#{METHODACCESS} void one(#{ARG[0]} b);\n" + + "#{METHODACCESS} void two(#{ARG[0]} a, #{ARG[1]} b);"), + THREE("#{METHODACCESS} void one(#{ARG[0]} a);\n" + + "#{METHODACCESS} void two(#{ARG[0]} a, #{ARG[1]} b);\n" + + "#{METHODACCESS} void three(#{ARG[0]} a, #{ARG[1]} b, #{ARG[0]} c);"); + + String methodsKindTemplate; + + MethodsKind(String methodsKindTemplate) { + this.methodsKindTemplate = methodsKindTemplate; + } + + @Override + public String expand(String optParameter) { + return methodsKindTemplate; + } + } + + /** + * Type of arguments to insert in method signatures + */ + enum ArgumentKind implements ComboParameter { + BOOLEAN("boolean"), + BYTE("byte"), + CHAR("char"), + SHORT("short"), + INT("int"), + LONG("long"), + FLOAT("float"), + DOUBLE("double"), + STRING("String"); + + String argumentsKindTemplate; + + ArgumentKind(String argumentsKindTemplate) { + this.argumentsKindTemplate = argumentsKindTemplate; + } + + @Override + public String expand(String optParameter) { + return argumentsKindTemplate; + } + } + + /** + * Exceptions to be added to zero and one methods. + */ + enum ExceptionKind implements ComboParameter { + NONE(null), + EXCEPTION(java.lang.Exception.class), + RUNTIME_EXCEPTION(java.lang.RuntimeException.class), + ILLEGAL_ARGUMENT_EXCEPTION(java.lang.IllegalArgumentException.class), + IOEXCEPTION(java.io.IOException.class), + /** + * Used only for throw testing, is empty for throws clause in the source, + */ + UNDECLARED_EXCEPTION(Exception1.class), + ; + + Class exceptionKindClass; + + ExceptionKind(Class exceptionKindClass) { + this.exceptionKindClass = exceptionKindClass; + } + + @Override + public String expand(String optParameter) { + return exceptionKindClass == null || exceptionKindClass == Exception1.class + ? "" : "throws " + exceptionKindClass.getName(); + } + } + + /** + * Extra interfaces to be added. + */ + enum MultiInterfacesKind implements ComboParameter { + NONE(new Class[0]), + INTERFACE_WITH_EXCEPTION(new Class[] {InterfaceWithException.class}), + ; + + Class[] multiInterfaceClasses; + + MultiInterfacesKind(Class[] multiInterfaceClasses) { + this.multiInterfaceClasses = multiInterfaceClasses; + } + + @Override + // Not used for expansion only execution + public String expand(String optParameter) { + throw new RuntimeException("NYI"); + } + + Class[] classes() { + return multiInterfaceClasses; + } + } + + @Override + public int id() { + return ++uniqueId; + } + + protected void fail(String msg, Throwable thrown) { + super.fail(msg); + thrown.printStackTrace(); + } + + /** + * Test interface with a "one(int)" method. + */ + interface InterfaceWithException { + // The signature must match the ONE MethodsKind above + void one(int a) throws RuntimeException, IOException; + } + + + /** + * Main to generate combinations and run the tests. + * @param args unused + * @throws Exception In case of failure + */ + public static void main(String... args) throws Exception { + + // Test variations of access declarations + new ComboTestHelper() + .withDimension("CLASSACCESS", ClassAccessKind.values()) + .withDimension("METHODACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC}) + .withDimension("METHODS", ProxyGeneratorCombo::saveMethod, + new MethodsKind[] {MethodsKind.NONE, MethodsKind.ZERO, MethodsKind.ONE}) + .withDimension("ARG[0]", new ArgumentKind[] {ArgumentKind.INT}) + .withDimension("EXCEPTION", ProxyGeneratorCombo::saveException, + new ExceptionKind[]{ExceptionKind.NONE}) + .run(ProxyGeneratorCombo::new); + + // Test variations of argument types + new ComboTestHelper() + .withDimension("CLASSACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC}) + .withDimension("METHODACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC}) + .withDimension("METHODS", ProxyGeneratorCombo::saveMethod, + MethodsKind.values()) + .withArrayDimension("ARG", ProxyGeneratorCombo::saveArg, 2, + ArgumentKind.values()) + .withDimension("EXCEPTION", ProxyGeneratorCombo::saveException, + new ExceptionKind[]{ExceptionKind.NONE}) + .withFilter(ProxyGeneratorCombo::filter) + .run(ProxyGeneratorCombo::new); + + // Test for conflicts in Exceptions on methods with the same signatures + new ComboTestHelper() + .withDimension("CLASSACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC}) + .withDimension("METHODACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC}) + .withDimension("METHODS", ProxyGeneratorCombo::saveMethod, new MethodsKind[] { + MethodsKind.ZERO}) + .withDimension("EXCEPTION", ProxyGeneratorCombo::saveException, + ExceptionKind.values()) + .withDimension("MULTI_INTERFACES", ProxyGeneratorCombo::saveInterface, + new MultiInterfacesKind[] {MultiInterfacesKind.NONE}) + .run(ProxyGeneratorCombo::new); + } + + /** + * Basic template. + */ + String template = "#{CLASSACCESS} interface #{TESTNAME} {\n" + + "#{METHODS}" + + "}"; + + // Saved values of Combo values + private MultiInterfacesKind currInterface = MultiInterfacesKind.NONE; + private MethodsKind currMethod = MethodsKind.NONE; + private ExceptionKind currException = ExceptionKind.NONE; + private ArgumentKind[] currArgs = new ArgumentKind[0]; + + void saveInterface(ComboParameter s) { + currInterface = (MultiInterfacesKind)s; + } + + void saveMethod(ComboParameter s) { + currMethod = (MethodsKind)s; + } + + void saveException(ComboParameter s) { + currException = (ExceptionKind)s; + } + + void saveArg(ComboParameter s, int index) { + if (index >= currArgs.length) { + currArgs = Arrays.copyOf(currArgs, index + 1); + } + currArgs[index] = (ArgumentKind)s; + } + + /** + * Filter out needless tests (mostly with more variations of arguments than needed). + * @return true to run the test, false if not + */ + boolean filter() { + if ((currMethod == MethodsKind.NONE || currMethod == MethodsKind.ZERO) && + currArgs.length >= 2) { + return currArgs[0] == ArgumentKind.INT && + currArgs[1] == ArgumentKind.INT; + } + if (currMethod == MethodsKind.ONE && + currArgs.length >= 2 ) { + return currArgs[0] == currArgs[1]; + } + return true; + } + + /** + * Generate the source file and compile. + * Generate a proxy for the interface and test the resulting Proxy + * for the methods, exceptions and handling of a thrown exception + * @throws IOException catch all IOException + */ + @Override + public void doWork() throws IOException { + String cp = System.getProperty("test.classes"); + String ifaceName = "Interface_" + this.id(); + newCompilationTask() + .withSourceFromTemplate(ifaceName, template.replace("#{TESTNAME}", ifaceName)) + .withOption("-d") + .withOption(cp) + .generate(this::checkCompile); + try { + ClassLoader loader = ClassLoader.getSystemClassLoader(); + Class tc = Class.forName(ifaceName); + InvocationHandler handler = + new ProxyHandler(currException.exceptionKindClass); + + // Construct array of interfaces for the proxy + Class[] interfaces = new Class[currInterface.classes().length + 1]; + interfaces[0] = tc; + System.arraycopy(currInterface.classes(), 0, + interfaces, 1, + currInterface.classes().length); + + Object proxy = Proxy.newProxyInstance(loader, interfaces, handler); + if (!Proxy.isProxyClass(proxy.getClass())) { + fail("generated proxy is not a proxy class"); + return; + } + for (Class i : interfaces) { + if (!i.isAssignableFrom(proxy.getClass())) { + fail("proxy is not assignable to " + i.getName()); + } + } + try { + String s = proxy.toString(); + } catch (Exception ex) { + ex.printStackTrace(); + fail("proxy.toString() threw an exception"); + } + + checkDeclaredProxyExceptions(proxy, interfaces); + + if (currMethod == MethodsKind.ZERO && currException != ExceptionKind.NONE) { + checkThrowsException(proxy, interfaces); + } + + } catch (Exception ex) { + throw new RuntimeException("doWork unexpected", ex); + } + } + + /** + * Check that the exceptions declared on the proxy match the declarations for + * exceptions from the interfaces. + * + * @param proxy a proxy object + * @param interfaces the interfaces that defined it + */ + void checkDeclaredProxyExceptions(Object proxy, Class[] interfaces) { + var allMethods = allMethods(Arrays.asList(interfaces)); + Method[] methods = proxy.getClass().getDeclaredMethods(); + for (Method m : methods) { + String sig = toShortSignature(m); + var imethods = allMethods.get(sig); + if (imethods != null) { + var expectedEx = Set.copyOf(Arrays.asList(m.getExceptionTypes())); + var exs = Set.copyOf(extractExceptions(imethods)); + if (!expectedEx.equals(exs)) { + System.out.printf("mismatch on exceptions for method %s:%nExpected: " + + "%s%nActual: %s%n", + sig, expectedEx, exs); + fail("Exceptions declared on proxy don't match interface methods"); + } + } + } + } + + void checkThrowsException(Object proxy, Class[] interfaces) { + ProxyHandler ph = (ProxyHandler)(Proxy.getInvocationHandler(proxy)); + try { + Method m = proxy.getClass().getDeclaredMethod("zero"); + m.invoke(proxy); + fail("Missing exception: " + ph.exceptionClass); + } catch (NoSuchMethodException nsme) { + System.out.printf("No method 'zero()' to test exceptions with%n"); + for (var cl : interfaces) { + System.out.printf(" i/f %s: %s%n", cl, Arrays.toString(cl.getMethods())); + } + Method[] methods = proxy.getClass().getMethods(); + System.out.printf(" Proxy methods: %s%n", Arrays.toString(methods)); + fail("No such method test bug", nsme); + } catch (InvocationTargetException actual) { + ph.checkThrownException(actual.getTargetException()); + } catch (IllegalAccessException iae) { + fail("IllegalAccessException", iae); + } + } + + /** + * Exceptions known to be supported by all methods with the same signature. + * @return a list of universal exception types + */ + private static List> extractExceptions(List methods) { + // for all methods with the same signature + // start with the exceptions from the first method + // while there are any exceptions remaining + // look at the next method + List> exceptions = null; + for (Method m : methods) { + var e = m.getExceptionTypes(); + if (e.length == 0) + return emptyClassList(); + List> elist = Arrays.asList(e); + if (exceptions == null) { + exceptions = elist; // initialize to first method exceptions + } else { + // for each exception + // if it is compatible (both ways) with any of the existing exceptions continue + // else remove the current exception + var okExceptions = new HashSet>(); + for (int j = 0; j < exceptions.size(); j++) { + var ex = exceptions.get(j); + for (int i = 0; i < elist.size();i++) { + var ci = elist.get(i); + + if (ci.isAssignableFrom(ex)) { + okExceptions.add(ex); + } + if (ex.isAssignableFrom(ci)) { + okExceptions.add(ci); + } + } + } + if (exceptions.isEmpty()) { + // The empty set terminates the search for a common set of exceptions + return emptyClassList(); + } + // Use the new set for the next iteration + exceptions = List.copyOf(okExceptions); + } + } + return (exceptions == null) ? emptyClassList() : exceptions; + } + + /** + * An empty correctly typed list of classes. + * @return An empty typed list of classes + */ + @SuppressWarnings("unchecked") + static List> emptyClassList() { + return Collections.EMPTY_LIST; + } + + /** + * Accumulate all of the unique methods. + * + * @param interfaces a list of interfaces + * @return a map from signature to List of methods, unique by signature + */ + private static Map> allMethods(List> interfaces) { + Map> methods = new HashMap<>(); + for (Class c : interfaces) { + for (Method m : c.getMethods()) { + if (!Modifier.isStatic(m.getModifiers())) { + String sig = toShortSignature(m); + methods.computeIfAbsent(sig, s -> new ArrayList()) + .add(m); + } + } + } + return methods; + } + + /** + * The signature of a method without the return type. + * @param m a Method + * @return the signature with method name and parameters + */ + static String toShortSignature(Method m) { + StringJoiner sj = new StringJoiner(",", m.getName() + "(", ")"); + for (Class parameterType : m.getParameterTypes()) { + sj.add(parameterType.getTypeName()); + } + return sj.toString(); + } + + /** + * Report any compilation errors. + * @param res the result + */ + void checkCompile(Result res) { + if (res.hasErrors()) { + fail("invalid diagnostics for source:\n" + + res.compilationInfo() + + "\nFound error: " + res.hasErrors()); + } + } + + /** + * The Handler for the proxy includes the method to invoke the proxy + * and the expected exception, if any. + */ + class ProxyHandler implements InvocationHandler { + + private final Class exceptionClass; + + ProxyHandler(Class throwable) { + this.exceptionClass = throwable; + } + + /** + * Invoke a method on the proxy or return a value. + * @param proxy the proxy instance that the method was invoked on + * @param method a method + * @param args some args + * @return + * @throws Throwable a throwable + */ + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("toString")) { + return "Proxy" + System.identityHashCode(proxy); + } + if (method.getName().equals("zero")) { + if (exceptionClass != null) { + throw exceptionClass.getDeclaredConstructor().newInstance(); + } + } + return "meth: " + method.toString(); + } + + /** + * Check that the expected exception was thrown. + * Special case is handled for Exception1 which does not appear in the + * throws clause of the method so UndeclaredThrowableException is expected. + */ + void checkThrownException(Throwable thrown) { + if (exceptionClass == Exception1.class && + thrown instanceof UndeclaredThrowableException && + ((UndeclaredThrowableException)thrown).getUndeclaredThrowable() instanceof Exception1) { + // Exception1 caused UndeclaredThrowableException + return; + } else if (exceptionClass == Exception1.class) { + fail("UndeclaredThrowableException", thrown); + } + + if (exceptionClass != null && + !exceptionClass.equals(thrown.getClass())) { + throw new RuntimeException("Wrong exception thrown: expected: " + exceptionClass + + ", actual: " + thrown.getClass()); + } + } + } + + /** + * Exception to be thrown as a test of InvocationTarget. + */ + static class Exception1 extends Exception { + private static final long serialVersionUID = 1L; + Exception1() {} + } +} --- /dev/null 2019-08-16 15:28:57.192077812 -0400 +++ new/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyBench.java 2019-08-21 16:28:44.008036192 -0400 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.reflect.proxy; + +import org.openjdk.jmh.annotations.*; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 5) +@Measurement(iterations = 10) +@Fork(value = 1) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class ProxyBench { + + /** + * On Dell T7610: + * + * Benchmark w/ -Djdk.proxy.ProxyGenerator.v49=true + * Benchmark Mode Cnt Score Error Units + * ProxyBench.getProxyClass1i avgt 10 20.472 +/- 0.209 ns/op + * ProxyBench.getProxyClass4i avgt 10 57.353 +/- 0.461 ns/op + * ProxyBench.newProxyInstance1i avgt 10 31.459 +/- 0.516 ns/op + * ProxyBench.newProxyInstance4i avgt 10 66.580 +/- 0.983 ns/op + * + * Benchmark Mode Cnt Score Error Units + * ProxyBench.getProxyClass1i avgt 10 21.291 +/- 0.475 ns/op + * ProxyBench.getProxyClass4i avgt 10 61.481 +/- 4.709 ns/op + * ProxyBench.newProxyInstance1i avgt 10 30.177 +/- 0.761 ns/op + * ProxyBench.newProxyInstance4i avgt 10 68.302 +/- 1.344 ns/op + */ + + interface PkgPrivate1 { + void m1(); + } + + interface PkgPrivate2 { + void m2(); + } + + static final InvocationHandler handler = (proxy, method, args) -> null; + + static final ClassLoader loader1 = null; + static final Class[] interfaces1 = {Runnable.class}; + + static final ClassLoader loader4 = PkgPrivate1.class.getClassLoader(); + static final Class[] interfaces4 = {Runnable.class, Callable.class, + PkgPrivate1.class, PkgPrivate2.class}; + + @Benchmark + public Class getProxyClass1i() { + return Proxy.getProxyClass(loader1, interfaces1); + } + + @Benchmark + public Class getProxyClass4i() { + return Proxy.getProxyClass(loader4, interfaces4); + } + + @Benchmark + public Object newProxyInstance1i() { + return Proxy.newProxyInstance(loader1, interfaces1, handler); + } + + @Benchmark + public Object newProxyInstance4i() { + return Proxy.newProxyInstance(loader4, interfaces4, handler); + } +} --- /dev/null 2019-08-16 15:28:57.192077812 -0400 +++ new/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyPerf.java 2019-08-21 16:28:44.432038295 -0400 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.reflect.proxy; + +import java.util.List; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import org.openjdk.jmh.infra.Blackhole; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.concurrent.TimeUnit; + +/** + * Benchmark measuring java.lang.reflect.ProxyGenerator.generateProxyClass. + * It bypasses the cache of proxies to measure the time to construct a proxy. + */ +@Warmup(iterations = 5) +@Measurement(iterations = 10) +@Fork(value = 1) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class ProxyPerf { + + /** + * Sample results from a Dell T7610. + * Benchmark Mode Cnt Score Error Units + * ProxyPerf.genIntf_1 avgt 10 35325.428 +/- 780.459 ns/op + * ProxyPerf.genIntf_1_V49 avgt 10 34309.423 +/- 727.188 ns/op + * ProxyPerf.genStringsIntf_3 avgt 10 46600.366 +/- 663.812 ns/op + * ProxyPerf.genStringsIntf_3_V49 avgt 10 45911.817 +/- 1598.536 ns/op + * ProxyPerf.genZeroParams avgt 10 33245.048 +/- 437.988 ns/op + * ProxyPerf.genZeroParams_V49 avgt 10 32954.254 +/- 1041.932 ns/op + * ProxyPerf.getPrimsIntf_2 avgt 10 43987.819 +/- 837.443 ns/op + * ProxyPerf.getPrimsIntf_2_V49 avgt 10 42863.462 +/- 1193.480 ns/op + */ + + public interface Intf_1 { + public Object mL(Object o); + } + + public interface Intf_2 { + public int m1I(int i); + public long m2IJ(int i, long l); + } + + public interface Intf_3 { + public void mString(String s1); + public String m2String(String s1); + public String m2String(String s1, String s2); + } + + private InvocationHandler handler; + private ClassLoader classloader; + private Method proxyGen; + private Method proxyGenV49; + + @Setup + public void setup() { + try { + handler = (Object proxy, Method method, Object[] args) -> null; + classloader = ClassLoader.getSystemClassLoader(); + Class proxyGenClass = Class.forName("java.lang.reflect.ProxyGenerator"); + proxyGen = proxyGenClass.getDeclaredMethod("generateProxyClass", + ClassLoader.class, String.class, java.util.List.class, int.class); + proxyGen.setAccessible(true); + + // Init access to the old Proxy generator + Class proxyGenClassV49 = Class.forName("java.lang.reflect.ProxyGenerator_v49"); + proxyGenV49 = proxyGenClassV49.getDeclaredMethod("generateProxyClass", + String.class, java.util.List.class, int.class); + proxyGenV49.setAccessible(true); + + } catch (Exception ex) { + ex.printStackTrace(); + throw new RuntimeException("ProxyClass setup fails", ex); + } + } + + @Benchmark + public void genZeroParams(Blackhole bh) throws Exception { + List> interfaces = List.of(Runnable.class); + bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + } + + @Benchmark + public void genIntf_1(Blackhole bh) throws Exception { + List> interfaces = List.of(Intf_1.class); + bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + } + + @Benchmark + public void getPrimsIntf_2(Blackhole bh) throws Exception { + List> interfaces = List.of(Intf_2.class); + bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + } + @Benchmark + public void genStringsIntf_3(Blackhole bh) throws Exception { + List> interfaces = List.of(Intf_3.class); + bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + } + + // Generate using the V49inal generator for comparison + + @Benchmark + public void genZeroParams_V49(Blackhole bh) throws Exception { + List> interfaces = List.of(Runnable.class); + bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); + } + + @Benchmark + public void genIntf_1_V49(Blackhole bh) throws Exception { + List> interfaces = List.of(Intf_1.class); + bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); + } + + @Benchmark + public void getPrimsIntf_2_V49(Blackhole bh) throws Exception { + List> interfaces = List.of(Intf_2.class); + bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); + } + @Benchmark + public void genStringsIntf_3_V49(Blackhole bh) throws Exception { + List> interfaces = List.of(Intf_3.class); + bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); + } + +}