--- a/src/java.base/share/classes/java/lang/reflect/Method.java Wed Aug 21 13:49:16 2019 -0700
+++ b/src/java.base/share/classes/java/lang/reflect/Method.java Wed Aug 21 16:19:17 2019 -0400
@@ -420,17 +420,16 @@
@Override
String toShortString() {
- StringBuilder sb = new StringBuilder("method ");
- sb.append(getDeclaringClass().getTypeName()).append('.');
- sb.append(getName());
- sb.append('(');
- StringJoiner sj = new StringJoiner(",");
+ return "method " + getDeclaringClass().getTypeName() +
+ '.' + toShortSignature();
+ }
+
+ String toShortSignature() {
+ StringJoiner sj = new StringJoiner(",", getName() + "(", ")");
for (Class<?> parameterType : getParameterTypes()) {
sj.add(parameterType.getTypeName());
}
- sb.append(sj);
- sb.append(')');
- return sb.toString();
+ return sj.toString();
}
/**
--- a/src/java.base/share/classes/java/lang/reflect/Proxy.java Wed Aug 21 13:49:16 2019 -0700
+++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java Wed Aug 21 16:19:17 2019 -0400
@@ -49,6 +49,7 @@
import jdk.internal.reflect.Reflection;
import jdk.internal.loader.ClassLoaderValue;
import sun.reflect.misc.ReflectUtil;
+import sun.security.action.GetBooleanAction;
import sun.security.action.GetPropertyAction;
import sun.security.util.SecurityConstants;
@@ -99,7 +100,7 @@
* <li>A proxy class extends {@code java.lang.reflect.Proxy}.
*
* <li>A proxy class implements exactly the interfaces specified at its
- * creation, in the same order. Invoking {@link Class#getInterfaces getInterfaces}
+ * creation, in the same order. Invoking {@link Class#getInterfaces() getInterfaces}
* on its {@code Class} object will return an array containing the same
* list of interfaces (in the order specified at its creation), invoking
* {@link Class#getMethods getMethods} on its {@code Class} object will return
@@ -296,6 +297,13 @@
new ClassLoaderValue<>();
/**
+ * System property to revert to generation of proxy class files for version 1.5 (V49).
+ * Set to "true" to generate v49 class file format.
+ */
+ private static final boolean PROXY_GENERATOR_V49 =
+ GetBooleanAction.privilegedGetProperty("jdk.proxy.ProxyGenerator.v49");
+
+ /**
* the invocation handler for this proxy instance.
* @serial
*/
@@ -531,8 +539,9 @@
/*
* Generate the specified proxy class.
*/
- byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
- proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
+ byte[] proxyClassFile = PROXY_GENERATOR_V49
+ ? ProxyGenerator_v49.generateProxyClass(proxyName, interfaces, accessFlags)
+ : ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
try {
Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
null, "__dynamic_proxy__");
@@ -1116,6 +1125,5 @@
return ih;
}
- private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
private static final String PROXY_PACKAGE_PREFIX = ReflectUtil.PROXY_PACKAGE;
}
--- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java Wed Aug 21 13:49:16 2019 -0700
+++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java Wed Aug 21 16:19:17 2019 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,302 +25,134 @@
package java.lang.reflect;
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.File;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import sun.security.action.GetBooleanAction;
+
import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.reflect.Array;
-import java.lang.reflect.Method;
+import java.lang.invoke.MethodType;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
-import sun.security.action.GetBooleanAction;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
* ProxyGenerator contains the code to generate a dynamic proxy class
* for the java.lang.reflect.Proxy API.
- *
- * The external interfaces to ProxyGenerator is the static
+ * <p>
+ * The external interface to ProxyGenerator is the static
* "generateProxyClass" method.
- *
- * @author Peter Jones
- * @since 1.3
*/
-class ProxyGenerator {
- /*
- * In the comments below, "JVMS" refers to The Java Virtual Machine
- * Specification Second Edition and "JLS" refers to the original
- * version of The Java Language Specification, unless otherwise
- * specified.
- */
-
- /* generate 1.5-era class file version */
- private static final int CLASSFILE_MAJOR_VERSION = 49;
- private static final int CLASSFILE_MINOR_VERSION = 0;
-
- /*
- * beginning of constants copied from
- * sun.tools.java.RuntimeConstants (which no longer exists):
- */
+final class ProxyGenerator extends ClassWriter {
- /* 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;
+ 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";
- /* 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;
+ 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;";
- // end of constants copied from sun.tools.java.RuntimeConstants
+ 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>";
- /** name of the superclass of proxy classes */
- private static final String superclassName = "java/lang/reflect/Proxy";
+ private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
- /** name of field for storing a proxy instance's invocation handler */
+ /**
+ * name of field for storing a proxy instance's invocation handler
+ */
private static final String handlerFieldName = "h";
- /** debugging flag for saving generated class files */
+ /**
+ * debugging flag for saving generated class files
+ */
private static final boolean saveGeneratedFiles =
- java.security.AccessController.doPrivileged(
- new GetBooleanAction(
- "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();
+ 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;
/**
- * Generate a public proxy class given a name and a list of proxy interfaces.
+ * 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.
*/
- static byte[] generateProxyClass(final String name,
- Class<?>[] interfaces) {
- return generateProxyClass(name, interfaces, (ACC_PUBLIC | ACC_FINAL | ACC_SUPER));
+ 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;
}
/**
@@ -329,295 +161,55 @@
* @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);
+ */
+ 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(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");
+ 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);
+ }
}
- 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.
+ * Return an array of the type names from an array of Classes.
*
- * 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.
+ * @param classes an array of classes or interfaces
+ * @return the array of class names; or null if there are no classes
*/
- 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));
+ 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;
}
/**
@@ -645,16 +237,15 @@
*/
LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<>();
- nextNewReturnType:
+ nextNewReturnType:
for (ProxyMethod pm : methods) {
Class<?> newReturnType = pm.returnType;
if (newReturnType.isPrimitive()) {
throw new IllegalArgumentException(
- "methods with same signature " +
- getFriendlyMethodSignature(pm.methodName,
- pm.parameterTypes) +
- " but incompatible return types: " +
- newReturnType.getName() + " and others");
+ "methods with same signature " +
+ pm.shortSignature +
+ " but incompatible return types: " +
+ newReturnType.getName() + " and others");
}
boolean added = false;
@@ -709,856 +300,9 @@
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;
- }
- }
- }
+ "methods with same signature " +
+ pm.shortSignature +
+ " but incompatible return types: " + uncoveredReturnTypes);
}
}
@@ -1585,13 +329,13 @@
*/
private static List<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) {
List<Class<?>> uniqueList = new ArrayList<>();
- // unique exceptions to catch
+ // unique exceptions to catch
uniqueList.add(Error.class); // always catch/rethrow these
uniqueList.add(RuntimeException.class);
- nextException:
- for (Class<?> ex: exceptions) {
+ nextException:
+ for (Class<?> ex : exceptions) {
if (ex.isAssignableFrom(Throwable.class)) {
/*
* If Throwable is declared to be thrown by the proxy method,
@@ -1610,7 +354,7 @@
* Compare this exception against the current list of
* exceptions that need to be caught:
*/
- for (int j = 0; j < uniqueList.size();) {
+ for (int j = 0; j < uniqueList.size(); ) {
Class<?> ex2 = uniqueList.get(j);
if (ex2.isAssignableFrom(ex)) {
/*
@@ -1635,28 +379,535 @@
}
/**
+ * 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 {
- /** "base type" used in various descriptors (see JVMS section 4.3.2) */
- public String baseTypeString;
-
- /** name of corresponding wrapper class */
- public String wrapperClassName;
+ private static Map<Class<?>, PrimitiveTypeInfo> table = new HashMap<>();
- /** 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);
@@ -1668,365 +919,46 @@
add(boolean.class, Boolean.class);
}
- private static void add(Class<?> primitiveClass, Class<?> wrapperClass) {
- table.put(primitiveClass,
- new PrimitiveTypeInfo(primitiveClass, wrapperClass));
- }
+ /**
+ * 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();
- baseTypeString =
- Array.newInstance(primitiveClass, 0)
- .getClass().getName().substring(1);
+ /**
+ * "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 + ";";
+ "(" + 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);
}
}
-
-
- /**
- * 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;
- }
- }
- }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator_v49.java Wed Aug 21 16:19:17 2019 -0400
@@ -0,0 +1,2031 @@
+/*
+ * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.reflect;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import sun.security.action.GetBooleanAction;
+
+/**
+ * ProxyGenerator contains the code to generate a dynamic proxy class
+ * for the java.lang.reflect.Proxy API.
+ *
+ * The external interfaces to ProxyGenerator is the static
+ * "generateProxyClass" method.
+ *
+ * @author Peter Jones
+ * @since 1.3
+ */
+class ProxyGenerator_v49 {
+ /*
+ * In the comments below, "JVMS" refers to The Java Virtual Machine
+ * Specification Second Edition and "JLS" refers to the original
+ * version of The Java Language Specification, unless otherwise
+ * specified.
+ */
+
+ /* generate 1.5-era class file version */
+ private static final int CLASSFILE_MAJOR_VERSION = 49;
+ private static final int CLASSFILE_MINOR_VERSION = 0;
+
+ /*
+ * beginning of constants copied from
+ * sun.tools.java.RuntimeConstants (which no longer exists):
+ */
+
+ /* constant pool tags */
+ private static final int CONSTANT_UTF8 = 1;
+ private static final int CONSTANT_UNICODE = 2;
+ private static final int CONSTANT_INTEGER = 3;
+ private static final int CONSTANT_FLOAT = 4;
+ private static final int CONSTANT_LONG = 5;
+ private static final int CONSTANT_DOUBLE = 6;
+ private static final int CONSTANT_CLASS = 7;
+ private static final int CONSTANT_STRING = 8;
+ private static final int CONSTANT_FIELD = 9;
+ private static final int CONSTANT_METHOD = 10;
+ private static final int CONSTANT_INTERFACEMETHOD = 11;
+ private static final int CONSTANT_NAMEANDTYPE = 12;
+
+ /* access and modifier flags */
+ private static final int ACC_PUBLIC = 0x00000001;
+ private static final int ACC_PRIVATE = 0x00000002;
+// private static final int ACC_PROTECTED = 0x00000004;
+ private static final int ACC_STATIC = 0x00000008;
+ private static final int ACC_FINAL = 0x00000010;
+// private static final int ACC_SYNCHRONIZED = 0x00000020;
+// private static final int ACC_VOLATILE = 0x00000040;
+// private static final int ACC_TRANSIENT = 0x00000080;
+// private static final int ACC_NATIVE = 0x00000100;
+// private static final int ACC_INTERFACE = 0x00000200;
+// private static final int ACC_ABSTRACT = 0x00000400;
+ private static final int ACC_SUPER = 0x00000020;
+// private static final int ACC_STRICT = 0x00000800;
+
+ /* opcodes */
+// private static final int opc_nop = 0;
+ private static final int opc_aconst_null = 1;
+// private static final int opc_iconst_m1 = 2;
+ private static final int opc_iconst_0 = 3;
+// private static final int opc_iconst_1 = 4;
+// private static final int opc_iconst_2 = 5;
+// private static final int opc_iconst_3 = 6;
+// private static final int opc_iconst_4 = 7;
+// private static final int opc_iconst_5 = 8;
+// private static final int opc_lconst_0 = 9;
+// private static final int opc_lconst_1 = 10;
+// private static final int opc_fconst_0 = 11;
+// private static final int opc_fconst_1 = 12;
+// private static final int opc_fconst_2 = 13;
+// private static final int opc_dconst_0 = 14;
+// private static final int opc_dconst_1 = 15;
+ private static final int opc_bipush = 16;
+ private static final int opc_sipush = 17;
+ private static final int opc_ldc = 18;
+ private static final int opc_ldc_w = 19;
+// private static final int opc_ldc2_w = 20;
+ private static final int opc_iload = 21;
+ private static final int opc_lload = 22;
+ private static final int opc_fload = 23;
+ private static final int opc_dload = 24;
+ private static final int opc_aload = 25;
+ private static final int opc_iload_0 = 26;
+// private static final int opc_iload_1 = 27;
+// private static final int opc_iload_2 = 28;
+// private static final int opc_iload_3 = 29;
+ private static final int opc_lload_0 = 30;
+// private static final int opc_lload_1 = 31;
+// private static final int opc_lload_2 = 32;
+// private static final int opc_lload_3 = 33;
+ private static final int opc_fload_0 = 34;
+// private static final int opc_fload_1 = 35;
+// private static final int opc_fload_2 = 36;
+// private static final int opc_fload_3 = 37;
+ private static final int opc_dload_0 = 38;
+// private static final int opc_dload_1 = 39;
+// private static final int opc_dload_2 = 40;
+// private static final int opc_dload_3 = 41;
+ private static final int opc_aload_0 = 42;
+// private static final int opc_aload_1 = 43;
+// private static final int opc_aload_2 = 44;
+// private static final int opc_aload_3 = 45;
+// private static final int opc_iaload = 46;
+// private static final int opc_laload = 47;
+// private static final int opc_faload = 48;
+// private static final int opc_daload = 49;
+// private static final int opc_aaload = 50;
+// private static final int opc_baload = 51;
+// private static final int opc_caload = 52;
+// private static final int opc_saload = 53;
+// private static final int opc_istore = 54;
+// private static final int opc_lstore = 55;
+// private static final int opc_fstore = 56;
+// private static final int opc_dstore = 57;
+ private static final int opc_astore = 58;
+// private static final int opc_istore_0 = 59;
+// private static final int opc_istore_1 = 60;
+// private static final int opc_istore_2 = 61;
+// private static final int opc_istore_3 = 62;
+// private static final int opc_lstore_0 = 63;
+// private static final int opc_lstore_1 = 64;
+// private static final int opc_lstore_2 = 65;
+// private static final int opc_lstore_3 = 66;
+// private static final int opc_fstore_0 = 67;
+// private static final int opc_fstore_1 = 68;
+// private static final int opc_fstore_2 = 69;
+// private static final int opc_fstore_3 = 70;
+// private static final int opc_dstore_0 = 71;
+// private static final int opc_dstore_1 = 72;
+// private static final int opc_dstore_2 = 73;
+// private static final int opc_dstore_3 = 74;
+ private static final int opc_astore_0 = 75;
+// private static final int opc_astore_1 = 76;
+// private static final int opc_astore_2 = 77;
+// private static final int opc_astore_3 = 78;
+// private static final int opc_iastore = 79;
+// private static final int opc_lastore = 80;
+// private static final int opc_fastore = 81;
+// private static final int opc_dastore = 82;
+ private static final int opc_aastore = 83;
+// private static final int opc_bastore = 84;
+// private static final int opc_castore = 85;
+// private static final int opc_sastore = 86;
+ private static final int opc_pop = 87;
+// private static final int opc_pop2 = 88;
+ private static final int opc_dup = 89;
+// private static final int opc_dup_x1 = 90;
+// private static final int opc_dup_x2 = 91;
+// private static final int opc_dup2 = 92;
+// private static final int opc_dup2_x1 = 93;
+// private static final int opc_dup2_x2 = 94;
+// private static final int opc_swap = 95;
+// private static final int opc_iadd = 96;
+// private static final int opc_ladd = 97;
+// private static final int opc_fadd = 98;
+// private static final int opc_dadd = 99;
+// private static final int opc_isub = 100;
+// private static final int opc_lsub = 101;
+// private static final int opc_fsub = 102;
+// private static final int opc_dsub = 103;
+// private static final int opc_imul = 104;
+// private static final int opc_lmul = 105;
+// private static final int opc_fmul = 106;
+// private static final int opc_dmul = 107;
+// private static final int opc_idiv = 108;
+// private static final int opc_ldiv = 109;
+// private static final int opc_fdiv = 110;
+// private static final int opc_ddiv = 111;
+// private static final int opc_irem = 112;
+// private static final int opc_lrem = 113;
+// private static final int opc_frem = 114;
+// private static final int opc_drem = 115;
+// private static final int opc_ineg = 116;
+// private static final int opc_lneg = 117;
+// private static final int opc_fneg = 118;
+// private static final int opc_dneg = 119;
+// private static final int opc_ishl = 120;
+// private static final int opc_lshl = 121;
+// private static final int opc_ishr = 122;
+// private static final int opc_lshr = 123;
+// private static final int opc_iushr = 124;
+// private static final int opc_lushr = 125;
+// private static final int opc_iand = 126;
+// private static final int opc_land = 127;
+// private static final int opc_ior = 128;
+// private static final int opc_lor = 129;
+// private static final int opc_ixor = 130;
+// private static final int opc_lxor = 131;
+// private static final int opc_iinc = 132;
+// private static final int opc_i2l = 133;
+// private static final int opc_i2f = 134;
+// private static final int opc_i2d = 135;
+// private static final int opc_l2i = 136;
+// private static final int opc_l2f = 137;
+// private static final int opc_l2d = 138;
+// private static final int opc_f2i = 139;
+// private static final int opc_f2l = 140;
+// private static final int opc_f2d = 141;
+// private static final int opc_d2i = 142;
+// private static final int opc_d2l = 143;
+// private static final int opc_d2f = 144;
+// private static final int opc_i2b = 145;
+// private static final int opc_i2c = 146;
+// private static final int opc_i2s = 147;
+// private static final int opc_lcmp = 148;
+// private static final int opc_fcmpl = 149;
+// private static final int opc_fcmpg = 150;
+// private static final int opc_dcmpl = 151;
+// private static final int opc_dcmpg = 152;
+// private static final int opc_ifeq = 153;
+// private static final int opc_ifne = 154;
+// private static final int opc_iflt = 155;
+// private static final int opc_ifge = 156;
+// private static final int opc_ifgt = 157;
+// private static final int opc_ifle = 158;
+// private static final int opc_if_icmpeq = 159;
+// private static final int opc_if_icmpne = 160;
+// private static final int opc_if_icmplt = 161;
+// private static final int opc_if_icmpge = 162;
+// private static final int opc_if_icmpgt = 163;
+// private static final int opc_if_icmple = 164;
+// private static final int opc_if_acmpeq = 165;
+// private static final int opc_if_acmpne = 166;
+// private static final int opc_goto = 167;
+// private static final int opc_jsr = 168;
+// private static final int opc_ret = 169;
+// private static final int opc_tableswitch = 170;
+// private static final int opc_lookupswitch = 171;
+ private static final int opc_ireturn = 172;
+ private static final int opc_lreturn = 173;
+ private static final int opc_freturn = 174;
+ private static final int opc_dreturn = 175;
+ private static final int opc_areturn = 176;
+ private static final int opc_return = 177;
+ private static final int opc_getstatic = 178;
+ private static final int opc_putstatic = 179;
+ private static final int opc_getfield = 180;
+// private static final int opc_putfield = 181;
+ private static final int opc_invokevirtual = 182;
+ private static final int opc_invokespecial = 183;
+ private static final int opc_invokestatic = 184;
+ private static final int opc_invokeinterface = 185;
+ private static final int opc_new = 187;
+// private static final int opc_newarray = 188;
+ private static final int opc_anewarray = 189;
+// private static final int opc_arraylength = 190;
+ private static final int opc_athrow = 191;
+ private static final int opc_checkcast = 192;
+// private static final int opc_instanceof = 193;
+// private static final int opc_monitorenter = 194;
+// private static final int opc_monitorexit = 195;
+ private static final int opc_wide = 196;
+// private static final int opc_multianewarray = 197;
+// private static final int opc_ifnull = 198;
+// private static final int opc_ifnonnull = 199;
+// private static final int opc_goto_w = 200;
+// private static final int opc_jsr_w = 201;
+
+ // end of constants copied from sun.tools.java.RuntimeConstants
+
+ /** name of the superclass of proxy classes */
+ private static final String superclassName = "java/lang/reflect/Proxy";
+
+ /** name of field for storing a proxy instance's invocation handler */
+ private static final String handlerFieldName = "h";
+
+ /** debugging flag for saving generated class files */
+ private static final boolean saveGeneratedFiles =
+ java.security.AccessController.doPrivileged(
+ new GetBooleanAction(
+ "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();
+
+ /**
+ * Generate a public proxy class given a name and a list of proxy interfaces.
+ */
+ static byte[] generateProxyClass(final String name,
+ List<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,
+ List<Class<?>> interfaces,
+ int accessFlags)
+ {
+ ProxyGenerator_v49 gen = new ProxyGenerator_v49(name, interfaces, accessFlags);
+ final byte[] classFile = gen.generateClassFile();
+
+ if (saveGeneratedFiles) {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<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 List<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 LinkedHashMap<>();
+
+ /** count of ProxyMethod objects added to proxyMethods */
+ private int proxyMethodCount = 0;
+
+ /**
+ * Construct a ProxyGenerator to generate a proxy class with the
+ * specified name and for the given interfaces.
+ *
+ * A ProxyGenerator object contains the state for the ongoing
+ * generation of a particular proxy class.
+ */
+ ProxyGenerator_v49(String className, List<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.size());
+ // u2 interfaces[interfaces_count];
+ for (Class<?> intf : interfaces) {
+ dout.writeShort(cp.getClass(
+ dotToSlash(intf.getName())));
+ }
+
+ // u2 fields_count;
+ dout.writeShort(fields.size());
+ // field_info fields[fields_count];
+ for (FieldInfo f : fields) {
+ f.write(dout);
+ }
+
+ // u2 methods_count;
+ dout.writeShort(methods.size());
+ // method_info methods[methods_count];
+ for (MethodInfo m : methods) {
+ m.write(dout);
+ }
+
+ // u2 attributes_count;
+ dout.writeShort(0); // (no ClassFile attributes for proxy classes)
+
+ } catch (IOException e) {
+ throw new InternalError("unexpected I/O Exception", e);
+ }
+
+ return bout.toByteArray();
+ }
+
+ /**
+ * Add another method to be proxied, either by creating a new
+ * ProxyMethod object or augmenting an old one for a duplicate
+ * method.
+ *
+ * "fromClass" indicates the proxy interface that the method was
+ * found through, which may be different from (a subinterface of)
+ * the method's "declaring class". Note that the first Method
+ * object passed for a given name and descriptor identifies the
+ * Method object (and thus the declaring class) that will be
+ * passed to the invocation handler's "invoke" method for a given
+ * set of duplicate methods.
+ */
+ private void addProxyMethod(Method m, Class<?> fromClass) {
+ String name = m.getName();
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ Class<?> returnType = m.getReturnType();
+ Class<?>[] exceptionTypes = m.getExceptionTypes();
+
+ String sig = name + getParameterDescriptors(parameterTypes);
+ List<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
+ * specification.
+ *
+ * Specifically, if there is more than one such method, then all
+ * of the return types must be reference types, and there must be
+ * one return type that is assignable to each of the rest of them.
+ */
+ private static void checkReturnTypes(List<ProxyMethod> methods) {
+ /*
+ * If there is only one method with a given signature, there
+ * cannot be a conflict. This is the only case in which a
+ * primitive (or void) return type is allowed.
+ */
+ if (methods.size() < 2) {
+ return;
+ }
+
+ /*
+ * List of return types that are not yet known to be
+ * assignable from ("covered" by) any of the others.
+ */
+ LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<>();
+
+ nextNewReturnType:
+ for (ProxyMethod pm : methods) {
+ Class<?> newReturnType = pm.returnType;
+ if (newReturnType.isPrimitive()) {
+ throw new IllegalArgumentException(
+ "methods with same signature " +
+ getFriendlyMethodSignature(pm.methodName,
+ pm.parameterTypes) +
+ " but incompatible return types: " +
+ newReturnType.getName() + " and others");
+ }
+ boolean added = false;
+
+ /*
+ * Compare the new return type to the existing uncovered
+ * return types.
+ */
+ ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();
+ while (liter.hasNext()) {
+ Class<?> uncoveredReturnType = liter.next();
+
+ /*
+ * If an existing uncovered return type is assignable
+ * to this new one, then we can forget the new one.
+ */
+ if (newReturnType.isAssignableFrom(uncoveredReturnType)) {
+ assert !added;
+ continue nextNewReturnType;
+ }
+
+ /*
+ * If the new return type is assignable to an existing
+ * uncovered one, then should replace the existing one
+ * with the new one (or just forget the existing one,
+ * if the new one has already be put in the list).
+ */
+ if (uncoveredReturnType.isAssignableFrom(newReturnType)) {
+ // (we can assume that each return type is unique)
+ if (!added) {
+ liter.set(newReturnType);
+ added = true;
+ } else {
+ liter.remove();
+ }
+ }
+ }
+
+ /*
+ * If we got through the list of existing uncovered return
+ * types without an assignability relationship, then add
+ * the new return type to the list of uncovered ones.
+ */
+ if (!added) {
+ uncoveredReturnTypes.add(newReturnType);
+ }
+ }
+
+ /*
+ * We shouldn't end up with more than one return type that is
+ * not assignable from any of the others.
+ */
+ if (uncoveredReturnTypes.size() > 1) {
+ ProxyMethod pm = methods.get(0);
+ throw new IllegalArgumentException(
+ "methods with same signature " +
+ getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) +
+ " but incompatible return types: " + uncoveredReturnTypes);
+ }
+ }
+
+ /**
+ * A FieldInfo object contains information about a particular field
+ * in the class being generated. The class mirrors the data items of
+ * the "field_info" structure of the class file format (see JVMS 4.5).
+ */
+ private class FieldInfo {
+ public int accessFlags;
+ public String name;
+ public String descriptor;
+
+ public FieldInfo(String name, String descriptor, int accessFlags) {
+ this.name = name;
+ this.descriptor = descriptor;
+ this.accessFlags = accessFlags;
+
+ /*
+ * Make sure that constant pool indexes are reserved for the
+ * following items before starting to write the final class file.
+ */
+ cp.getUtf8(name);
+ cp.getUtf8(descriptor);
+ }
+
+ public void write(DataOutputStream out) throws IOException {
+ /*
+ * Write all the items of the "field_info" structure.
+ * See JVMS section 4.5.
+ */
+ // u2 access_flags;
+ out.writeShort(accessFlags);
+ // u2 name_index;
+ out.writeShort(cp.getUtf8(name));
+ // u2 descriptor_index;
+ out.writeShort(cp.getUtf8(descriptor));
+ // u2 attributes_count;
+ out.writeShort(0); // (no field_info attributes for proxy classes)
+ }
+ }
+
+ /**
+ * An ExceptionTableEntry object holds values for the data items of
+ * an entry in the "exception_table" item of the "Code" attribute of
+ * "method_info" structures (see JVMS 4.7.3).
+ */
+ private static class ExceptionTableEntry {
+ public short startPc;
+ public short endPc;
+ public short handlerPc;
+ public short catchType;
+
+ public ExceptionTableEntry(short startPc, short endPc,
+ short handlerPc, short catchType)
+ {
+ this.startPc = startPc;
+ this.endPc = endPc;
+ this.handlerPc = handlerPc;
+ this.catchType = catchType;
+ }
+ };
+
+ /**
+ * A MethodInfo object contains information about a particular method
+ * in the class being generated. This class mirrors the data items of
+ * the "method_info" structure of the class file format (see JVMS 4.6).
+ */
+ private class MethodInfo {
+ public int accessFlags;
+ public String name;
+ public String descriptor;
+ public short maxStack;
+ public short maxLocals;
+ public ByteArrayOutputStream code = new ByteArrayOutputStream();
+ public List<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);
+ add(int.class, Integer.class);
+ add(long.class, Long.class);
+ add(short.class, Short.class);
+ add(boolean.class, Boolean.class);
+ }
+
+ private static void add(Class<?> primitiveClass, Class<?> wrapperClass) {
+ table.put(primitiveClass,
+ new PrimitiveTypeInfo(primitiveClass, wrapperClass));
+ }
+
+ private PrimitiveTypeInfo(Class<?> primitiveClass, Class<?> wrapperClass) {
+ assert primitiveClass.isPrimitive();
+
+ baseTypeString =
+ Array.newInstance(primitiveClass, 0)
+ .getClass().getName().substring(1);
+ wrapperClassName = dotToSlash(wrapperClass.getName());
+ wrapperValueOfDesc =
+ "(" + baseTypeString + ")L" + wrapperClassName + ";";
+ unwrapMethodName = primitiveClass.getName() + "Value";
+ unwrapMethodDesc = "()" + baseTypeString;
+ }
+
+ public static PrimitiveTypeInfo get(Class<?> cl) {
+ return table.get(cl);
+ }
+ }
+
+
+ /**
+ * A ConstantPool object represents the constant pool of a class file
+ * being generated. This representation of a constant pool is designed
+ * specifically for use by ProxyGenerator; in particular, it assumes
+ * that constant pool entries will not need to be resorted (for example,
+ * by their type, as the Java compiler does), so that the final index
+ * value can be assigned and used when an entry is first created.
+ *
+ * Note that new entries cannot be created after the constant pool has
+ * been written to a class file. To prevent such logic errors, a
+ * ConstantPool instance can be marked "read only", so that further
+ * attempts to add new entries will fail with a runtime exception.
+ *
+ * See JVMS section 4.4 for more information about the constant pool
+ * of a class file.
+ */
+ private static class ConstantPool {
+
+ /**
+ * list of constant pool entries, in constant pool index order.
+ *
+ * This list is used when writing the constant pool to a stream
+ * and for assigning the next index value. Note that element 0
+ * of this list corresponds to constant pool index 1.
+ */
+ private List<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;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/reflect/Proxy/ProxyGeneratorCombo.java Wed Aug 21 16:19:17 2019 -0400
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Proxy Generator Combo tests
+ * @library /test/langtools/tools/javac/lib .
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.code
+ * jdk.compiler/com.sun.tools.javac.comp
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.tree
+ * jdk.compiler/com.sun.tools.javac.util
+ * @build combo.ComboTestHelper
+ * @run main/othervm ProxyGeneratorCombo
+ * @run main/othervm -Djdk.proxy.ProxyGenerator.v49=true ProxyGeneratorCombo
+ */
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
+
+import combo.ComboInstance;
+import combo.ComboParameter;
+import combo.ComboTask.Result;
+import combo.ComboTestHelper;
+
+public class ProxyGeneratorCombo extends ComboInstance<ProxyGeneratorCombo> {
+
+ // The unique number to qualify interface names, unique across multiple runs
+ private static int uniqueId = 0;
+
+ /**
+ * Class Access kinds.
+ */
+ enum ClassAccessKind implements ComboParameter {
+ PUBLIC("public"),
+ PACKAGE(""),
+ ;
+
+ String classAccessTemplate;
+
+ ClassAccessKind(String classAccessTemplate) {
+ this.classAccessTemplate = classAccessTemplate;
+ }
+
+ @Override
+ public String expand(String optParameter) {
+ return classAccessTemplate;
+ }
+ }
+
+ /**
+ * Signatures of methods to be tested.
+ */
+ enum MethodsKind implements ComboParameter {
+ NONE(""),
+ ZERO("#{METHODACCESS} void zero() #{EXCEPTION};"),
+ ONE("#{METHODACCESS} void one(#{ARG[0]} a) #{EXCEPTION};"),
+ TWO("#{METHODACCESS} void one(#{ARG[0]} b);\n" +
+ "#{METHODACCESS} void two(#{ARG[0]} a, #{ARG[1]} b);"),
+ THREE("#{METHODACCESS} void one(#{ARG[0]} a);\n" +
+ "#{METHODACCESS} void two(#{ARG[0]} a, #{ARG[1]} b);\n" +
+ "#{METHODACCESS} void three(#{ARG[0]} a, #{ARG[1]} b, #{ARG[0]} c);");
+
+ String methodsKindTemplate;
+
+ MethodsKind(String methodsKindTemplate) {
+ this.methodsKindTemplate = methodsKindTemplate;
+ }
+
+ @Override
+ public String expand(String optParameter) {
+ return methodsKindTemplate;
+ }
+ }
+
+ /**
+ * Type of arguments to insert in method signatures
+ */
+ enum ArgumentKind implements ComboParameter {
+ BOOLEAN("boolean"),
+ BYTE("byte"),
+ CHAR("char"),
+ SHORT("short"),
+ INT("int"),
+ LONG("long"),
+ FLOAT("float"),
+ DOUBLE("double"),
+ STRING("String");
+
+ String argumentsKindTemplate;
+
+ ArgumentKind(String argumentsKindTemplate) {
+ this.argumentsKindTemplate = argumentsKindTemplate;
+ }
+
+ @Override
+ public String expand(String optParameter) {
+ return argumentsKindTemplate;
+ }
+ }
+
+ /**
+ * Exceptions to be added to zero and one methods.
+ */
+ enum ExceptionKind implements ComboParameter {
+ NONE(null),
+ EXCEPTION(java.lang.Exception.class),
+ RUNTIME_EXCEPTION(java.lang.RuntimeException.class),
+ ILLEGAL_ARGUMENT_EXCEPTION(java.lang.IllegalArgumentException.class),
+ IOEXCEPTION(java.io.IOException.class),
+ /**
+ * Used only for throw testing, is empty for throws clause in the source,
+ */
+ UNDECLARED_EXCEPTION(Exception1.class),
+ ;
+
+ Class<? extends Throwable> exceptionKindClass;
+
+ ExceptionKind(Class<? extends Throwable> exceptionKindClass) {
+ this.exceptionKindClass = exceptionKindClass;
+ }
+
+ @Override
+ public String expand(String optParameter) {
+ return exceptionKindClass == null || exceptionKindClass == Exception1.class
+ ? "" : "throws " + exceptionKindClass.getName();
+ }
+ }
+
+ /**
+ * Extra interfaces to be added.
+ */
+ enum MultiInterfacesKind implements ComboParameter {
+ NONE(new Class<?>[0]),
+ INTERFACE_WITH_EXCEPTION(new Class<?>[] {InterfaceWithException.class}),
+ ;
+
+ Class<?>[] multiInterfaceClasses;
+
+ MultiInterfacesKind(Class<?>[] multiInterfaceClasses) {
+ this.multiInterfaceClasses = multiInterfaceClasses;
+ }
+
+ @Override
+ // Not used for expansion only execution
+ public String expand(String optParameter) {
+ throw new RuntimeException("NYI");
+ }
+
+ Class<?>[] classes() {
+ return multiInterfaceClasses;
+ }
+ }
+
+ @Override
+ public int id() {
+ return ++uniqueId;
+ }
+
+ protected void fail(String msg, Throwable thrown) {
+ super.fail(msg);
+ thrown.printStackTrace();
+ }
+
+ /**
+ * Test interface with a "one(int)" method.
+ */
+ interface InterfaceWithException {
+ // The signature must match the ONE MethodsKind above
+ void one(int a) throws RuntimeException, IOException;
+ }
+
+
+ /**
+ * Main to generate combinations and run the tests.
+ * @param args unused
+ * @throws Exception In case of failure
+ */
+ public static void main(String... args) throws Exception {
+
+ // Test variations of access declarations
+ new ComboTestHelper<ProxyGeneratorCombo>()
+ .withDimension("CLASSACCESS", ClassAccessKind.values())
+ .withDimension("METHODACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC})
+ .withDimension("METHODS", ProxyGeneratorCombo::saveMethod,
+ new MethodsKind[] {MethodsKind.NONE, MethodsKind.ZERO, MethodsKind.ONE})
+ .withDimension("ARG[0]", new ArgumentKind[] {ArgumentKind.INT})
+ .withDimension("EXCEPTION", ProxyGeneratorCombo::saveException,
+ new ExceptionKind[]{ExceptionKind.NONE})
+ .run(ProxyGeneratorCombo::new);
+
+ // Test variations of argument types
+ new ComboTestHelper<ProxyGeneratorCombo>()
+ .withDimension("CLASSACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC})
+ .withDimension("METHODACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC})
+ .withDimension("METHODS", ProxyGeneratorCombo::saveMethod,
+ MethodsKind.values())
+ .withArrayDimension("ARG", ProxyGeneratorCombo::saveArg, 2,
+ ArgumentKind.values())
+ .withDimension("EXCEPTION", ProxyGeneratorCombo::saveException,
+ new ExceptionKind[]{ExceptionKind.NONE})
+ .withFilter(ProxyGeneratorCombo::filter)
+ .run(ProxyGeneratorCombo::new);
+
+ // Test for conflicts in Exceptions on methods with the same signatures
+ new ComboTestHelper<ProxyGeneratorCombo>()
+ .withDimension("CLASSACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC})
+ .withDimension("METHODACCESS", new ClassAccessKind[]{ClassAccessKind.PUBLIC})
+ .withDimension("METHODS", ProxyGeneratorCombo::saveMethod, new MethodsKind[] {
+ MethodsKind.ZERO})
+ .withDimension("EXCEPTION", ProxyGeneratorCombo::saveException,
+ ExceptionKind.values())
+ .withDimension("MULTI_INTERFACES", ProxyGeneratorCombo::saveInterface,
+ new MultiInterfacesKind[] {MultiInterfacesKind.NONE})
+ .run(ProxyGeneratorCombo::new);
+ }
+
+ /**
+ * Basic template.
+ */
+ String template = "#{CLASSACCESS} interface #{TESTNAME} {\n" +
+ "#{METHODS}" +
+ "}";
+
+ // Saved values of Combo values
+ private MultiInterfacesKind currInterface = MultiInterfacesKind.NONE;
+ private MethodsKind currMethod = MethodsKind.NONE;
+ private ExceptionKind currException = ExceptionKind.NONE;
+ private ArgumentKind[] currArgs = new ArgumentKind[0];
+
+ void saveInterface(ComboParameter s) {
+ currInterface = (MultiInterfacesKind)s;
+ }
+
+ void saveMethod(ComboParameter s) {
+ currMethod = (MethodsKind)s;
+ }
+
+ void saveException(ComboParameter s) {
+ currException = (ExceptionKind)s;
+ }
+
+ void saveArg(ComboParameter s, int index) {
+ if (index >= currArgs.length) {
+ currArgs = Arrays.copyOf(currArgs, index + 1);
+ }
+ currArgs[index] = (ArgumentKind)s;
+ }
+
+ /**
+ * Filter out needless tests (mostly with more variations of arguments than needed).
+ * @return true to run the test, false if not
+ */
+ boolean filter() {
+ if ((currMethod == MethodsKind.NONE || currMethod == MethodsKind.ZERO) &&
+ currArgs.length >= 2) {
+ return currArgs[0] == ArgumentKind.INT &&
+ currArgs[1] == ArgumentKind.INT;
+ }
+ if (currMethod == MethodsKind.ONE &&
+ currArgs.length >= 2 ) {
+ return currArgs[0] == currArgs[1];
+ }
+ return true;
+ }
+
+ /**
+ * Generate the source file and compile.
+ * Generate a proxy for the interface and test the resulting Proxy
+ * for the methods, exceptions and handling of a thrown exception
+ * @throws IOException catch all IOException
+ */
+ @Override
+ public void doWork() throws IOException {
+ String cp = System.getProperty("test.classes");
+ String ifaceName = "Interface_" + this.id();
+ newCompilationTask()
+ .withSourceFromTemplate(ifaceName, template.replace("#{TESTNAME}", ifaceName))
+ .withOption("-d")
+ .withOption(cp)
+ .generate(this::checkCompile);
+ try {
+ ClassLoader loader = ClassLoader.getSystemClassLoader();
+ Class<?> tc = Class.forName(ifaceName);
+ InvocationHandler handler =
+ new ProxyHandler(currException.exceptionKindClass);
+
+ // Construct array of interfaces for the proxy
+ Class<?>[] interfaces = new Class<?>[currInterface.classes().length + 1];
+ interfaces[0] = tc;
+ System.arraycopy(currInterface.classes(), 0,
+ interfaces, 1,
+ currInterface.classes().length);
+
+ Object proxy = Proxy.newProxyInstance(loader, interfaces, handler);
+ if (!Proxy.isProxyClass(proxy.getClass())) {
+ fail("generated proxy is not a proxy class");
+ return;
+ }
+ for (Class<?> i : interfaces) {
+ if (!i.isAssignableFrom(proxy.getClass())) {
+ fail("proxy is not assignable to " + i.getName());
+ }
+ }
+ try {
+ String s = proxy.toString();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ fail("proxy.toString() threw an exception");
+ }
+
+ checkDeclaredProxyExceptions(proxy, interfaces);
+
+ if (currMethod == MethodsKind.ZERO && currException != ExceptionKind.NONE) {
+ checkThrowsException(proxy, interfaces);
+ }
+
+ } catch (Exception ex) {
+ throw new RuntimeException("doWork unexpected", ex);
+ }
+ }
+
+ /**
+ * Check that the exceptions declared on the proxy match the declarations for
+ * exceptions from the interfaces.
+ *
+ * @param proxy a proxy object
+ * @param interfaces the interfaces that defined it
+ */
+ void checkDeclaredProxyExceptions(Object proxy, Class<?>[] interfaces) {
+ var allMethods = allMethods(Arrays.asList(interfaces));
+ Method[] methods = proxy.getClass().getDeclaredMethods();
+ for (Method m : methods) {
+ String sig = toShortSignature(m);
+ var imethods = allMethods.get(sig);
+ if (imethods != null) {
+ var expectedEx = Set.copyOf(Arrays.asList(m.getExceptionTypes()));
+ var exs = Set.copyOf(extractExceptions(imethods));
+ if (!expectedEx.equals(exs)) {
+ System.out.printf("mismatch on exceptions for method %s:%nExpected: " +
+ "%s%nActual: %s%n",
+ sig, expectedEx, exs);
+ fail("Exceptions declared on proxy don't match interface methods");
+ }
+ }
+ }
+ }
+
+ void checkThrowsException(Object proxy, Class<?>[] interfaces) {
+ ProxyHandler ph = (ProxyHandler)(Proxy.getInvocationHandler(proxy));
+ try {
+ Method m = proxy.getClass().getDeclaredMethod("zero");
+ m.invoke(proxy);
+ fail("Missing exception: " + ph.exceptionClass);
+ } catch (NoSuchMethodException nsme) {
+ System.out.printf("No method 'zero()' to test exceptions with%n");
+ for (var cl : interfaces) {
+ System.out.printf(" i/f %s: %s%n", cl, Arrays.toString(cl.getMethods()));
+ }
+ Method[] methods = proxy.getClass().getMethods();
+ System.out.printf(" Proxy methods: %s%n", Arrays.toString(methods));
+ fail("No such method test bug", nsme);
+ } catch (InvocationTargetException actual) {
+ ph.checkThrownException(actual.getTargetException());
+ } catch (IllegalAccessException iae) {
+ fail("IllegalAccessException", iae);
+ }
+ }
+
+ /**
+ * Exceptions known to be supported by all methods with the same signature.
+ * @return a list of universal exception types
+ */
+ private static List<Class<?>> extractExceptions(List<Method> methods) {
+ // for all methods with the same signature
+ // start with the exceptions from the first method
+ // while there are any exceptions remaining
+ // look at the next method
+ List<Class<?>> exceptions = null;
+ for (Method m : methods) {
+ var e = m.getExceptionTypes();
+ if (e.length == 0)
+ return emptyClassList();
+ List<Class<?>> elist = Arrays.asList(e);
+ if (exceptions == null) {
+ exceptions = elist; // initialize to first method exceptions
+ } else {
+ // for each exception
+ // if it is compatible (both ways) with any of the existing exceptions continue
+ // else remove the current exception
+ var okExceptions = new HashSet<Class<?>>();
+ for (int j = 0; j < exceptions.size(); j++) {
+ var ex = exceptions.get(j);
+ for (int i = 0; i < elist.size();i++) {
+ var ci = elist.get(i);
+
+ if (ci.isAssignableFrom(ex)) {
+ okExceptions.add(ex);
+ }
+ if (ex.isAssignableFrom(ci)) {
+ okExceptions.add(ci);
+ }
+ }
+ }
+ if (exceptions.isEmpty()) {
+ // The empty set terminates the search for a common set of exceptions
+ return emptyClassList();
+ }
+ // Use the new set for the next iteration
+ exceptions = List.copyOf(okExceptions);
+ }
+ }
+ return (exceptions == null) ? emptyClassList() : exceptions;
+ }
+
+ /**
+ * An empty correctly typed list of classes.
+ * @return An empty typed list of classes
+ */
+ @SuppressWarnings("unchecked")
+ static List<Class<?>> emptyClassList() {
+ return Collections.EMPTY_LIST;
+ }
+
+ /**
+ * Accumulate all of the unique methods.
+ *
+ * @param interfaces a list of interfaces
+ * @return a map from signature to List of methods, unique by signature
+ */
+ private static Map<String, List<Method>> allMethods(List<Class<?>> interfaces) {
+ Map<String, List<Method>> methods = new HashMap<>();
+ for (Class<?> c : interfaces) {
+ for (Method m : c.getMethods()) {
+ if (!Modifier.isStatic(m.getModifiers())) {
+ String sig = toShortSignature(m);
+ methods.computeIfAbsent(sig, s -> new ArrayList<Method>())
+ .add(m);
+ }
+ }
+ }
+ return methods;
+ }
+
+ /**
+ * The signature of a method without the return type.
+ * @param m a Method
+ * @return the signature with method name and parameters
+ */
+ static String toShortSignature(Method m) {
+ StringJoiner sj = new StringJoiner(",", m.getName() + "(", ")");
+ for (Class<?> parameterType : m.getParameterTypes()) {
+ sj.add(parameterType.getTypeName());
+ }
+ return sj.toString();
+ }
+
+ /**
+ * Report any compilation errors.
+ * @param res the result
+ */
+ void checkCompile(Result<?> res) {
+ if (res.hasErrors()) {
+ fail("invalid diagnostics for source:\n" +
+ res.compilationInfo() +
+ "\nFound error: " + res.hasErrors());
+ }
+ }
+
+ /**
+ * The Handler for the proxy includes the method to invoke the proxy
+ * and the expected exception, if any.
+ */
+ class ProxyHandler implements InvocationHandler {
+
+ private final Class<? extends Throwable> exceptionClass;
+
+ ProxyHandler(Class<? extends Throwable> throwable) {
+ this.exceptionClass = throwable;
+ }
+
+ /**
+ * Invoke a method on the proxy or return a value.
+ * @param proxy the proxy instance that the method was invoked on
+ * @param method a method
+ * @param args some args
+ * @return
+ * @throws Throwable a throwable
+ */
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getName().equals("toString")) {
+ return "Proxy" + System.identityHashCode(proxy);
+ }
+ if (method.getName().equals("zero")) {
+ if (exceptionClass != null) {
+ throw exceptionClass.getDeclaredConstructor().newInstance();
+ }
+ }
+ return "meth: " + method.toString();
+ }
+
+ /**
+ * Check that the expected exception was thrown.
+ * Special case is handled for Exception1 which does not appear in the
+ * throws clause of the method so UndeclaredThrowableException is expected.
+ */
+ void checkThrownException(Throwable thrown) {
+ if (exceptionClass == Exception1.class &&
+ thrown instanceof UndeclaredThrowableException &&
+ ((UndeclaredThrowableException)thrown).getUndeclaredThrowable() instanceof Exception1) {
+ // Exception1 caused UndeclaredThrowableException
+ return;
+ } else if (exceptionClass == Exception1.class) {
+ fail("UndeclaredThrowableException", thrown);
+ }
+
+ if (exceptionClass != null &&
+ !exceptionClass.equals(thrown.getClass())) {
+ throw new RuntimeException("Wrong exception thrown: expected: " + exceptionClass +
+ ", actual: " + thrown.getClass());
+ }
+ }
+ }
+
+ /**
+ * Exception to be thrown as a test of InvocationTarget.
+ */
+ static class Exception1 extends Exception {
+ private static final long serialVersionUID = 1L;
+ Exception1() {}
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyBench.java Wed Aug 21 16:19:17 2019 -0400
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.bench.java.lang.reflect.proxy;
+
+import org.openjdk.jmh.annotations.*;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+@Fork(value = 1)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Thread)
+public class ProxyBench {
+
+ /**
+ * On Dell T7610:
+ *
+ * Benchmark w/ -Djdk.proxy.ProxyGenerator.v49=true
+ * Benchmark Mode Cnt Score Error Units
+ * ProxyBench.getProxyClass1i avgt 10 20.472 +/- 0.209 ns/op
+ * ProxyBench.getProxyClass4i avgt 10 57.353 +/- 0.461 ns/op
+ * ProxyBench.newProxyInstance1i avgt 10 31.459 +/- 0.516 ns/op
+ * ProxyBench.newProxyInstance4i avgt 10 66.580 +/- 0.983 ns/op
+ *
+ * Benchmark Mode Cnt Score Error Units
+ * ProxyBench.getProxyClass1i avgt 10 21.291 +/- 0.475 ns/op
+ * ProxyBench.getProxyClass4i avgt 10 61.481 +/- 4.709 ns/op
+ * ProxyBench.newProxyInstance1i avgt 10 30.177 +/- 0.761 ns/op
+ * ProxyBench.newProxyInstance4i avgt 10 68.302 +/- 1.344 ns/op
+ */
+
+ interface PkgPrivate1 {
+ void m1();
+ }
+
+ interface PkgPrivate2 {
+ void m2();
+ }
+
+ static final InvocationHandler handler = (proxy, method, args) -> null;
+
+ static final ClassLoader loader1 = null;
+ static final Class<?>[] interfaces1 = {Runnable.class};
+
+ static final ClassLoader loader4 = PkgPrivate1.class.getClassLoader();
+ static final Class<?>[] interfaces4 = {Runnable.class, Callable.class,
+ PkgPrivate1.class, PkgPrivate2.class};
+
+ @Benchmark
+ public Class<?> getProxyClass1i() {
+ return Proxy.getProxyClass(loader1, interfaces1);
+ }
+
+ @Benchmark
+ public Class<?> getProxyClass4i() {
+ return Proxy.getProxyClass(loader4, interfaces4);
+ }
+
+ @Benchmark
+ public Object newProxyInstance1i() {
+ return Proxy.newProxyInstance(loader1, interfaces1, handler);
+ }
+
+ @Benchmark
+ public Object newProxyInstance4i() {
+ return Proxy.newProxyInstance(loader4, interfaces4, handler);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyPerf.java Wed Aug 21 16:19:17 2019 -0400
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.bench.java.lang.reflect.proxy;
+
+import java.util.List;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+import org.openjdk.jmh.infra.Blackhole;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Benchmark measuring java.lang.reflect.ProxyGenerator.generateProxyClass.
+ * It bypasses the cache of proxies to measure the time to construct a proxy.
+ */
+@Warmup(iterations = 5)
+@Measurement(iterations = 10)
+@Fork(value = 1)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Thread)
+public class ProxyPerf {
+
+ /**
+ * Sample results from a Dell T7610.
+ * Benchmark Mode Cnt Score Error Units
+ * ProxyPerf.genIntf_1 avgt 10 35325.428 +/- 780.459 ns/op
+ * ProxyPerf.genIntf_1_V49 avgt 10 34309.423 +/- 727.188 ns/op
+ * ProxyPerf.genStringsIntf_3 avgt 10 46600.366 +/- 663.812 ns/op
+ * ProxyPerf.genStringsIntf_3_V49 avgt 10 45911.817 +/- 1598.536 ns/op
+ * ProxyPerf.genZeroParams avgt 10 33245.048 +/- 437.988 ns/op
+ * ProxyPerf.genZeroParams_V49 avgt 10 32954.254 +/- 1041.932 ns/op
+ * ProxyPerf.getPrimsIntf_2 avgt 10 43987.819 +/- 837.443 ns/op
+ * ProxyPerf.getPrimsIntf_2_V49 avgt 10 42863.462 +/- 1193.480 ns/op
+ */
+
+ public interface Intf_1 {
+ public Object mL(Object o);
+ }
+
+ public interface Intf_2 {
+ public int m1I(int i);
+ public long m2IJ(int i, long l);
+ }
+
+ public interface Intf_3 {
+ public void mString(String s1);
+ public String m2String(String s1);
+ public String m2String(String s1, String s2);
+ }
+
+ private InvocationHandler handler;
+ private ClassLoader classloader;
+ private Method proxyGen;
+ private Method proxyGenV49;
+
+ @Setup
+ public void setup() {
+ try {
+ handler = (Object proxy, Method method, Object[] args) -> null;
+ classloader = ClassLoader.getSystemClassLoader();
+ Class<?> proxyGenClass = Class.forName("java.lang.reflect.ProxyGenerator");
+ proxyGen = proxyGenClass.getDeclaredMethod("generateProxyClass",
+ ClassLoader.class, String.class, java.util.List.class, int.class);
+ proxyGen.setAccessible(true);
+
+ // Init access to the old Proxy generator
+ Class<?> proxyGenClassV49 = Class.forName("java.lang.reflect.ProxyGenerator_v49");
+ proxyGenV49 = proxyGenClassV49.getDeclaredMethod("generateProxyClass",
+ String.class, java.util.List.class, int.class);
+ proxyGenV49.setAccessible(true);
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ throw new RuntimeException("ProxyClass setup fails", ex);
+ }
+ }
+
+ @Benchmark
+ public void genZeroParams(Blackhole bh) throws Exception {
+ List<Class<?>> interfaces = List.of(Runnable.class);
+ bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1));
+ }
+
+ @Benchmark
+ public void genIntf_1(Blackhole bh) throws Exception {
+ List<Class<?>> interfaces = List.of(Intf_1.class);
+ bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1));
+ }
+
+ @Benchmark
+ public void getPrimsIntf_2(Blackhole bh) throws Exception {
+ List<Class<?>> interfaces = List.of(Intf_2.class);
+ bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1));
+ }
+ @Benchmark
+ public void genStringsIntf_3(Blackhole bh) throws Exception {
+ List<Class<?>> interfaces = List.of(Intf_3.class);
+ bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1));
+ }
+
+ // Generate using the V49inal generator for comparison
+
+ @Benchmark
+ public void genZeroParams_V49(Blackhole bh) throws Exception {
+ List<Class<?>> interfaces = List.of(Runnable.class);
+ bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1));
+ }
+
+ @Benchmark
+ public void genIntf_1_V49(Blackhole bh) throws Exception {
+ List<Class<?>> interfaces = List.of(Intf_1.class);
+ bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1));
+ }
+
+ @Benchmark
+ public void getPrimsIntf_2_V49(Blackhole bh) throws Exception {
+ List<Class<?>> interfaces = List.of(Intf_2.class);
+ bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1));
+ }
+ @Benchmark
+ public void genStringsIntf_3_V49(Blackhole bh) throws Exception {
+ List<Class<?>> interfaces = List.of(Intf_3.class);
+ bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1));
+ }
+
+}