< prev index next >

src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java

Print this page

        

*** 1,7 **** /* ! * 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 --- 1,7 ---- /* ! * 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
*** 23,625 **** * 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<Void>() { ! 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<FieldInfo> fields = new ArrayList<>(); ! /** MethodInfo struct for each method of generated class */ ! private List<MethodInfo> methods = new ArrayList<>(); /** ! * maps method signature string to list of ProxyMethod objects for ! * proxy methods with that signature */ ! private Map<String, List<ProxyMethod>> 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<ProxyMethod> 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<ProxyMethod> 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<ProxyMethod> 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<Class<?>> 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 --- 23,217 ---- * questions. */ package java.lang.reflect; ! import jdk.internal.org.objectweb.asm.ClassWriter; ! import jdk.internal.org.objectweb.asm.Label; ! import jdk.internal.org.objectweb.asm.MethodVisitor; ! import jdk.internal.org.objectweb.asm.Opcodes; ! import sun.security.action.GetBooleanAction; ! import java.io.IOException; ! 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 static jdk.internal.org.objectweb.asm.Opcodes.*; /** * ProxyGenerator contains the code to generate a dynamic proxy class * for the java.lang.reflect.Proxy API. ! * <p> ! * The external interface to ProxyGenerator is the static * "generateProxyClass" method. */ ! final class ProxyGenerator extends ClassWriter { ! 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"; ! private static final String JLR_INVOCATION_HANDLER = "java/lang/reflect/InvocationHandler"; ! private static final String JLR_PROXY = "java/lang/reflect/Proxy"; ! private static final String JLR_UNDECLARED_THROWABLE_EX = "java/lang/reflect/UndeclaredThrowableException"; ! private static final 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;"; ! private static final String MJLR_INVOCATIONHANDLER = "(Ljava/lang/reflect/InvocationHandler;)V"; ! private static final String NAME_CTOR = "<init>"; ! private static final String NAME_CLINIT = "<clinit>"; ! private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; /** ! * 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")); ! /* Preloaded ProxyMethod objects for methods in java.lang.Object */ ! private final static ProxyMethod hashCodeMethod; ! private final static ProxyMethod equalsMethod; ! private final static ProxyMethod toStringMethod; static { try { ! hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), "m0"); ! equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), "m1"); ! toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), "m2"); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } ! /** ! * Class loader ! */ ! private final ClassLoader loader; ! /** ! * Name of proxy class ! */ ! private final String className; ! /** ! * Proxy interfaces ! */ ! private final List<Class<?>> interfaces; ! /** ! * Proxy class access flags ! */ ! private final int accessFlags; /** ! * 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 final Map<String, List<ProxyMethod>> proxyMethods = new LinkedHashMap<>(); ! /** ! * 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. ! * <p> * A ProxyGenerator object contains the state for the ongoing * generation of a particular proxy class. */ ! private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces, ! int accessFlags) { ! super(ClassWriter.COMPUTE_FRAMES); ! this.loader = loader; this.className = className; this.interfaces = interfaces; this.accessFlags = accessFlags; } /** ! * 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(ClassLoader loader, ! final String name, ! List<Class<?>> interfaces, ! int accessFlags) { ! ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); ! final byte[] classFile = gen.generateClassFile(); ! if (saveGeneratedFiles) { ! java.security.AccessController.doPrivileged( ! new java.security.PrivilegedAction<Void>() { ! 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 classFile; } /** ! * Return an array of the type names from an array of Classes. * ! * @param classes an array of classes or interfaces ! * @return the array of class names; or null if there are no classes */ ! private static String[] typeNames(List<Class<?>> 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; } /** * For a given set of proxy methods with the same signature, check * that their return types are compatible according to the Proxy
*** 649,660 **** 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; --- 241,251 ---- for (ProxyMethod pm : methods) { Class<?> newReturnType = pm.returnType; if (newReturnType.isPrimitive()) { throw new IllegalArgumentException( "methods with same signature " + ! pm.shortSignature + " but incompatible return types: " + newReturnType.getName() + " and others"); } boolean added = false;
*** 708,1664 **** */ 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<ExceptionTableEntry> exceptionTable = ! new ArrayList<ExceptionTableEntry>(); ! 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<Class<?>> 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", ! "<init>", "(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( ! "<init>", "(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, ! "<init>", "(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( - "<clinit>", "()V", ACC_STATIC); - - int localSlot0 = 1; - short pc, tryBegin = 0, tryEnd; - - DataOutputStream out = new DataOutputStream(minfo.code); - - for (List<ProxyMethod> 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", "<init>", "(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", - "<init>", "(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_<i>", "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<Class<?>> 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<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) { - List<Class<?>> 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<Class<?>,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); --- 299,915 ---- */ if (uncoveredReturnTypes.size() > 1) { ProxyMethod pm = methods.get(0); throw new IllegalArgumentException( "methods with same signature " + ! pm.shortSignature + " but incompatible return types: " + uncoveredReturnTypes); } } /** ! * 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<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) { ! List<Class<?>> 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; + } /** ! * 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. ! * <p> ! * Note that the original version of the JVMS contained a definition of ! * this abstract notion of a "word" in section 3.4, but that definition ! * was removed for the second edition. ! */ ! private static int getWordsPerType(Class<?> type) { ! if (type == long.class || type == double.class) { ! return 2; ! } else { ! return 1; ! } ! } ! ! /** ! * Add to the given list all of the types in the "from" array that ! * are not already contained in the list and are assignable to at ! * least one of the types in the "with" array. ! * <p> ! * 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<Class<?>> list) { ! for (Class<?> fc : from) { ! if (!list.contains(fc)) { ! for (Class<?> wc : with) { ! if (wc.isAssignableFrom(fc)) { ! list.add(fc); ! break; ! } ! } ! } ! } ! } ! ! /** ! * 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); ! } ! } ! } /* ! * For each set of proxy methods with the same signature, ! * verify that the methods' return types are compatible. */ ! for (List<ProxyMethod> sigmethods : proxyMethods.values()) { ! checkReturnTypes(sigmethods); ! } ! ! generateConstructor(); ! ! for (List<ProxyMethod> 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); ! } } ! generateStaticInitializer(); ! ! return 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) { ! Class<?> returnType = m.getReturnType(); ! Class<?>[] exceptionTypes = m.getExceptionTypes(); ! ! String sig = m.toShortSignature(); ! List<ProxyMethod> 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<Class<?>> 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<ProxyMethod> 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<ProxyMethod> 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, + "<init>", "(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, + "<init>", "(Ljava/lang/String;)V", false); + mv.visitInsn(ATHROW); + + // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored + mv.visitMaxs(-1, -1); + mv.visitEnd(); } /** * 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 static class ProxyMethod { ! 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(Method method, String sig, Class<?>[] parameterTypes, Class<?> returnType, Class<?>[] exceptionTypes, ! Class<?> fromClass, String methodFieldName) { ! this.method = method; ! this.shortSignature = sig; this.parameterTypes = parameterTypes; this.returnType = returnType; this.exceptionTypes = exceptionTypes; this.fromClass = fromClass; ! this.methodFieldName = methodFieldName; } /** ! * 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 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; for (int i = 0; i < parameterSlot.length; i++) { parameterSlot[i] = nextSlot; nextSlot += getWordsPerType(parameterTypes[i]); } ! mv.visitCode(); ! Label L_startBlock = new Label(); ! Label L_endBlock = new Label(); ! Label L_RuntimeHandler = new Label(); ! Label L_ThrowableHandler = new Label(); ! List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes); ! if (catchList.size() > 0) { ! for (Class<?> ex : catchList) { ! mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler, ! dotToSlash(ex.getName())); ! } ! mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler, ! JL_THROWABLE); ! } ! mv.visitLabel(L_startBlock); ! mv.visitVarInsn(ALOAD, 0); ! mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName, ! LJLR_INVOCATION_HANDLER); ! mv.visitVarInsn(ALOAD, 0); ! mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName, ! LJLR_METHOD); if (parameterTypes.length > 0) { ! // Create an array and fill with the parameters converting primitives to wrappers ! emitIconstInsn(mv, parameterTypes.length); ! mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT); for (int i = 0; i < parameterTypes.length; i++) { ! mv.visitInsn(DUP); ! emitIconstInsn(mv, i); ! codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]); ! mv.visitInsn(Opcodes.AASTORE); } } else { ! mv.visitInsn(Opcodes.ACONST_NULL); } ! mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER, "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + ! "[Ljava/lang/Object;)Ljava/lang/Object;", true); if (returnType == void.class) { ! mv.visitInsn(POP); ! mv.visitInsn(RETURN); } else { ! codeUnwrapReturnValue(mv, returnType); } ! mv.visitLabel(L_endBlock); ! // Generate exception handler ! mv.visitLabel(L_RuntimeHandler); ! mv.visitInsn(ATHROW); // just rethrow the exception ! ! mv.visitLabel(L_ThrowableHandler); ! mv.visitVarInsn(ASTORE, 1); ! mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX); ! mv.visitInsn(DUP); ! mv.visitVarInsn(ALOAD, 1); ! mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX, ! "<init>", "(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. */ ! 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) { ! mv.visitVarInsn(ILOAD, slot); } else if (type == long.class) { ! mv.visitVarInsn(LLOAD, slot); } else if (type == float.class) { ! mv.visitVarInsn(FLOAD, slot); } else if (type == double.class) { ! mv.visitVarInsn(DLOAD, slot); } else { throw new AssertionError(); } ! mv.visitMethodInsn(INVOKESTATIC, prim.wrapperClassName, "valueOf", ! prim.wrapperValueOfDesc, false); } else { ! 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. */ ! private void codeUnwrapReturnValue(MethodVisitor mv, Class<?> type) { if (type.isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); ! mv.visitTypeInsn(CHECKCAST, prim.wrapperClassName); ! mv.visitMethodInsn(INVOKEVIRTUAL, prim.wrapperClassName, ! prim.unwrapMethodName, prim.unwrapMethodDesc, false); if (type == int.class || type == boolean.class || type == byte.class || type == char.class || ! type == short.class) { ! mv.visitInsn(IRETURN); } else if (type == long.class) { ! mv.visitInsn(LRETURN); } else if (type == float.class) { ! mv.visitInsn(FRETURN); } else if (type == double.class) { ! mv.visitInsn(DRETURN); } else { throw new AssertionError(); } } else { ! 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. */ ! private void codeFieldInitialization(MethodVisitor mv, String className) { ! codeClassForName(mv, fromClass); ! mv.visitLdcInsn(method.getName()); ! emitIconstInsn(mv, parameterTypes.length); ! 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++) { ! mv.visitInsn(DUP); ! emitIconstInsn(mv, i); if (parameterTypes[i].isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]); ! mv.visitFieldInsn(GETSTATIC, ! prim.wrapperClassName, "TYPE", LJL_CLASS); } else { ! codeClassForName(mv, parameterTypes[i]); } ! 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); } /* * =============== Code Generation Utility Methods =============== */ /** * 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); } /** ! * Visit a bytecode for a constant. * ! * @param mv The MethodVisitor ! * @param cst The constant value */ ! private void emitIconstInsn(MethodVisitor mv, final int cst) { ! if (cst >= -1 && cst <= 5) { ! mv.visitInsn(Opcodes.ICONST_0 + cst); ! } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { ! mv.visitIntInsn(Opcodes.BIPUSH, cst); ! } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { ! mv.visitIntInsn(Opcodes.SIPUSH, cst); } else { ! mv.visitLdcInsn(cst); } } ! @Override ! public String toString() { ! return method.toShortString(); } } /** * 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 { ! private static Map<Class<?>, 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);
*** 1666,2032 **** 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<Entry> 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<Object,Integer> 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; ! } } } } --- 917,964 ---- add(long.class, Long.class); add(short.class, Short.class); add(boolean.class, Boolean.class); } /** ! * name of corresponding wrapper class */ ! private String wrapperClassName; /** ! * method descriptor for wrapper class "valueOf" factory method */ ! private String wrapperValueOfDesc; /** ! * name of wrapper class method for retrieving primitive value */ ! private String unwrapMethodName; /** ! * descriptor of same method */ ! private String unwrapMethodDesc; ! private PrimitiveTypeInfo(Class<?> primitiveClass, Class<?> wrapperClass) { ! assert primitiveClass.isPrimitive(); /** ! * "base type" used in various descriptors (see JVMS section 4.3.2) */ ! String baseTypeString = ! Array.newInstance(primitiveClass, 0) ! .getClass().getName().substring(1); ! wrapperClassName = dotToSlash(wrapperClass.getName()); ! wrapperValueOfDesc = ! "(" + baseTypeString + ")L" + wrapperClassName + ";"; ! unwrapMethodName = primitiveClass.getName() + "Value"; ! unwrapMethodDesc = "()" + baseTypeString; } ! private static void add(Class<?> primitiveClass, Class<?> wrapperClass) { ! table.put(primitiveClass, ! new PrimitiveTypeInfo(primitiveClass, wrapperClass)); } ! public static PrimitiveTypeInfo get(Class<?> cl) { ! return table.get(cl); } } }
< prev index next >