< 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 >