jdk/src/java.base/share/classes/java/lang/reflect/Proxy.java
changeset 36972 27147cde3256
parent 36669 17af8b44d311
child 37349 648609dc0f3d
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Proxy.java	Mon Apr 11 16:46:52 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Proxy.java	Mon Apr 11 10:55:03 2016 +0200
@@ -25,7 +25,6 @@
 
 package java.lang.reflect;
 
-import java.lang.ref.WeakReference;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Arrays;
@@ -39,10 +38,8 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.WeakHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.BiFunction;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -284,6 +281,13 @@
         { InvocationHandler.class };
 
     /**
+     * a cache of proxy constructors with
+     * {@link Constructor#setAccessible(boolean) accessible} flag already set
+     */
+    private static final ClassLoaderValue<Constructor<?>> proxyCache =
+        new ClassLoaderValue<>();
+
+    /**
      * the invocation handler for this proxy instance.
      * @serial
      */
@@ -361,14 +365,55 @@
                                          Class<?>... interfaces)
         throws IllegalArgumentException
     {
-        final List<Class<?>> intfs = List.of(interfaces);  // interfaces cloned
-        final SecurityManager sm = System.getSecurityManager();
-        final Class<?> caller = Reflection.getCallerClass();
-        if (sm != null) {
-            checkProxyAccess(caller, loader, intfs);
+        Class<?> caller = System.getSecurityManager() == null
+                              ? null
+                              : Reflection.getCallerClass();
+
+        return getProxyConstructor(caller, loader, interfaces)
+            .getDeclaringClass();
+    }
+
+    /**
+     * Returns the {@code Constructor} object of a proxy class that takes a
+     * single argument of type {@link InvocationHandler}, given a class loader
+     * and an array of interfaces. The returned constructor will have the
+     * {@link Constructor#setAccessible(boolean) accessible} flag already set.
+     *
+     * @param   caller passed from a public-facing @CallerSensitive method if
+     *                 SecurityManager is set or {@code null} if there's no
+     *                 SecurityManager
+     * @param   loader the class loader to define the proxy class
+     * @param   interfaces the list of interfaces for the proxy class
+     *          to implement
+     * @return  a Constructor of the proxy class taking single
+     *          {@code InvocationHandler} parameter
+     */
+    private static Constructor<?> getProxyConstructor(Class<?> caller,
+                                                      ClassLoader loader,
+                                                      Class<?>... interfaces)
+    {
+        // optimization for single interface
+        if (interfaces.length == 1) {
+            Class<?> intf = interfaces[0];
+            if (caller != null) {
+                checkProxyAccess(caller, loader, intf);
+            }
+            return proxyCache.sub(intf).computeIfAbsent(
+                loader,
+                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
+            );
+        } else {
+            // interfaces cloned
+            final Class<?>[] intfsArray = interfaces.clone();
+            if (caller != null) {
+                checkProxyAccess(caller, loader, intfsArray);
+            }
+            final List<Class<?>> intfs = Arrays.asList(intfsArray);
+            return proxyCache.sub(intfs).computeIfAbsent(
+                loader,
+                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
+            );
         }
-
-        return new ProxyBuilder(loader, intfs).build();
     }
 
     /*
@@ -391,7 +436,7 @@
      */
     private static void checkProxyAccess(Class<?> caller,
                                          ClassLoader loader,
-                                         List<Class<?>> interfaces)
+                                         Class<?> ... interfaces)
     {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
@@ -399,147 +444,18 @@
             if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) {
                 sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
             }
-            ReflectUtil.checkProxyPackageAccess(ccl, interfaces.toArray(EMPTY_CLASS_ARRAY));
-        }
-    }
-
-    /*
-     * a key used for proxy class with 0 implemented interfaces
-     */
-    private static final Object key0 = new Object();
-
-    /*
-     * Key1 and Key2 are optimized for the common use of dynamic proxies
-     * that implement 1 or 2 interfaces.
-     */
-
-    /*
-     * a key used for proxy class with 1 implemented interface
-     */
-    private static final class Key1 extends WeakReference<Class<?>> {
-        private final int hash;
-
-        Key1(Class<?> intf) {
-            super(intf);
-            this.hash = intf.hashCode();
-        }
-
-        @Override
-        public int hashCode() {
-            return hash;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            Class<?> intf;
-            return this == obj ||
-                   obj != null &&
-                   obj.getClass() == Key1.class &&
-                   (intf = get()) != null &&
-                   intf == ((Key1) obj).get();
-        }
-    }
-
-    /*
-     * a key used for proxy class with 2 implemented interfaces
-     */
-    private static final class Key2 extends WeakReference<Class<?>> {
-        private final int hash;
-        private final WeakReference<Class<?>> ref2;
-
-        Key2(Class<?> intf1, Class<?> intf2) {
-            super(intf1);
-            hash = 31 * intf1.hashCode() + intf2.hashCode();
-            ref2 = new WeakReference<>(intf2);
-        }
-
-        @Override
-        public int hashCode() {
-            return hash;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            Class<?> intf1, intf2;
-            return this == obj ||
-                   obj != null &&
-                   obj.getClass() == Key2.class &&
-                   (intf1 = get()) != null &&
-                   intf1 == ((Key2) obj).get() &&
-                   (intf2 = ref2.get()) != null &&
-                   intf2 == ((Key2) obj).ref2.get();
-        }
-    }
-
-    /*
-     * a key used for proxy class with any number of implemented interfaces
-     * (used here for 3 or more only)
-     */
-    private static final class KeyX {
-        private final int hash;
-        private final WeakReference<Class<?>>[] refs;
-
-        @SuppressWarnings("unchecked")
-        KeyX(List<Class<?>> interfaces) {
-            hash = Arrays.hashCode(interfaces.toArray());
-            refs = (WeakReference<Class<?>>[])new WeakReference<?>[interfaces.size()];
-            int i = 0;
-            for (Class<?> intf : interfaces) {
-                refs[i++] = new WeakReference<>(intf);
-            }
-        }
-
-        @Override
-        public int hashCode() {
-            return hash;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            return this == obj ||
-                   obj != null &&
-                   obj.getClass() == KeyX.class &&
-                   equals(refs, ((KeyX) obj).refs);
-        }
-
-        private static boolean equals(WeakReference<Class<?>>[] refs1,
-                                      WeakReference<Class<?>>[] refs2) {
-            if (refs1.length != refs2.length) {
-                return false;
-            }
-            for (int i = 0; i < refs1.length; i++) {
-                Class<?> intf = refs1[i].get();
-                if (intf == null || intf != refs2[i].get()) {
-                    return false;
-                }
-            }
-            return true;
+            ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
         }
     }
 
     /**
-     * A function that maps an array of interfaces to an optimal key where
-     * Class objects representing interfaces are weakly referenced.
+     * Builder for a proxy class.
+     *
+     * If the module is not specified in this ProxyBuilder constructor,
+     * it will map from the given loader and interfaces to the module
+     * in which the proxy class will be defined.
      */
-    private static final class KeyFactory<T>
-        implements BiFunction<T, List<Class<?>>, Object>
-    {
-        @Override
-        public Object apply(T t, List<Class<?>> interfaces) {
-            switch (interfaces.size()) {
-                case 1: return new Key1(interfaces.get(0)); // the most frequent
-                case 2: return new Key2(interfaces.get(0), interfaces.get(1));
-                case 0: return key0;
-                default: return new KeyX(interfaces);
-            }
-        }
-    }
-
-    /**
-     * A factory function that generates, defines and returns the proxy class
-     * given the ClassLoader and array of interfaces.
-     */
-    private static final class ProxyClassFactory {
+    private static final class ProxyBuilder {
         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
 
         // prefix for all proxy class names
@@ -548,6 +464,10 @@
         // next number to use for generation of unique proxy class names
         private static final AtomicLong nextUniqueNumber = new AtomicLong();
 
+        // a reverse cache of defined proxy classes
+        private static final ClassLoaderValue<Boolean> reverseProxyCache =
+            new ClassLoaderValue<>();
+
         private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
             String proxyPkg = null;     // package to define proxy class in
             int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
@@ -601,8 +521,11 @@
             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                     proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
             try {
-                return UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length,
-                                          loader, null);
+                Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile,
+                                                 0, proxyClassFile.length,
+                                                 loader, null);
+                reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
+                return pc;
             } catch (ClassFormatError e) {
                 /*
                  * A ClassFormatError here means that (barring bugs in the
@@ -616,35 +539,14 @@
         }
 
         /**
-         * Test if the given class is a proxy class
+         * Test if given class is a class defined by
+         * {@link #defineProxyClass(Module, List)}
          */
         static boolean isProxyClass(Class<?> c) {
-            return proxyCache.containsValue(c);
-        }
-
-        /**
-         * Returns the proxy class.  It will return the cached proxy class
-         * if exists; otherwise, it will create the proxy class and store in
-         * the cache.
-         */
-        static Class<?> get(Module module, List<Class<?>> interfaces) {
-            return proxyCache.get(module, interfaces);
+            return Objects.equals(reverseProxyCache.sub(c).get(c.getClassLoader()),
+                                  Boolean.TRUE);
         }
 
-        /**
-         * a cache of proxy classes in the named and unnamed module
-         */
-        private static final WeakCache<Module, List<Class<?>>, Class<?>> proxyCache =
-            new WeakCache<>(new KeyFactory<Module>(),
-                new BiFunction<Module, List<Class<?>>, Class<?>>()  {
-                    @Override
-                    public Class<?> apply(Module m, List<Class<?>> interfaces) {
-                        Objects.requireNonNull(m);
-                        return defineProxyClass(m, interfaces);
-                    }
-            });
-
-
         private static boolean isExportedType(Class<?> c) {
             String pn = c.getPackageName();
             return Modifier.isPublic(c.getModifiers()) && c.getModule().isExported(pn);
@@ -685,25 +587,18 @@
                 }
             });
 
-        private static final boolean isDebug() {
+        private static boolean isDebug() {
             return !DEBUG.isEmpty();
         }
-        private static final boolean isDebug(String flag) {
+        private static boolean isDebug(String flag) {
             return DEBUG.equals(flag);
         }
-    }
+
+        // ProxyBuilder instance members start here....
 
-    /**
-     * Builder for a proxy class.
-     *
-     * If the module is not specified in this ProxyBuilder constructor,
-     * it will map from the given loader and interfaces to the module
-     * in which the proxy class will be defined.
-     */
-    private static final class ProxyBuilder {
-        final ClassLoader loader;
-        final List<Class<?>> interfaces;
-        final Module module;
+        private final ClassLoader loader;
+        private final List<Class<?>> interfaces;
+        private final Module module;
         ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
             if (!VM.isModuleSystemInited()) {
                 throw new InternalError("Proxy is not supported until module system is fully initialzed");
@@ -723,16 +618,34 @@
             assert getLoader(module) == loader;
         }
 
+        ProxyBuilder(ClassLoader loader, Class<?> intf) {
+            this(loader, Collections.singletonList(intf));
+        }
+
         /**
-         * Generate a proxy class.  If the target module does not have any
+         * Generate a proxy class and return its proxy Constructor with
+         * accessible flag already set. If the target module does not have access
          * to any interface types, IllegalAccessError will be thrown by the VM
          * at defineClass time.
          *
          * Must call the checkProxyAccess method to perform permission checks
          * before calling this.
          */
-        Class<?> build() {
-            return ProxyClassFactory.get(module, interfaces);
+        Constructor<?> build() {
+            Class<?> proxyClass = defineProxyClass(module, interfaces);
+            final Constructor<?> cons;
+            try {
+                cons = proxyClass.getConstructor(constructorParams);
+            } catch (NoSuchMethodException e) {
+                throw new InternalError(e.toString(), e);
+            }
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                public Void run() {
+                    cons.setAccessible(true);
+                    return null;
+                }
+            });
+            return cons;
         }
 
         /**
@@ -742,9 +655,9 @@
          * @throws IllegalArgumentException if it violates the restrictions specified
          *         in {@link Proxy#newProxyInstance}
          */
-        static void validateProxyInterfaces(ClassLoader loader,
-                                            List<Class<?>> interfaces,
-                                            Set<Class<?>> refTypes)
+        private static void validateProxyInterfaces(ClassLoader loader,
+                                                    List<Class<?>> interfaces,
+                                                    Set<Class<?>> refTypes)
         {
             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.size());
             for (Class<?> intf : interfaces) {
@@ -779,10 +692,11 @@
          * Returns all types referenced by all public method signatures of
          * the proxy interfaces
          */
-        static Set<Class<?>> referencedTypes(ClassLoader loader, List<Class<?>> interfaces) {
+        private static Set<Class<?>> referencedTypes(ClassLoader loader,
+                                                     List<Class<?>> interfaces) {
             return interfaces.stream()
                  .flatMap(intf -> Stream.of(intf.getMethods())
-                                        .flatMap(m -> methodRefTypes(m))
+                                        .flatMap(ProxyBuilder::methodRefTypes)
                                         .map(ProxyBuilder::getElementType)
                                         .filter(t -> !t.isPrimitive()))
                  .collect(Collectors.toSet());
@@ -792,11 +706,11 @@
          * Extracts all types referenced on a method signature including
          * its return type, parameter types, and exception types.
          */
-        static Stream<Class<?>> methodRefTypes(Method m) {
+        private static Stream<Class<?>> methodRefTypes(Method m) {
             return Stream.of(new Class<?>[] { m.getReturnType() },
                              m.getParameterTypes(),
                              m.getExceptionTypes())
-                         .flatMap(a -> Stream.of(a));
+                         .flatMap(Stream::of);
         }
 
         /**
@@ -813,7 +727,9 @@
          * package.  Reads edge and qualified exports are added for
          * dynamic module to access.
          */
-        static Module mapToModule(ClassLoader loader, List<Class<?>> interfaces, Set<Class<?>> refTypes) {
+        private static Module mapToModule(ClassLoader loader,
+                                          List<Class<?>> interfaces,
+                                          Set<Class<?>> refTypes) {
             Map<Class<?>, Module> modulePrivateTypes = new HashMap<>();
             Map<Class<?>, Module> packagePrivateTypes = new HashMap<>();
             for (Class<?> intf : interfaces) {
@@ -884,10 +800,9 @@
             Set<Class<?>> visited = new HashSet<>();
             while (!deque.isEmpty()) {
                 Class<?> c = deque.poll();
-                if (visited.contains(c)) {
+                if (!visited.add(c)) {
                     continue;
                 }
-                visited.add(c);
                 ensureAccess(target, c);
 
                 // add all superinterfaces
@@ -906,7 +821,7 @@
         /*
          * Ensure the given module can access the given class.
          */
-        static void ensureAccess(Module target, Class<?> c) {
+        private static void ensureAccess(Module target, Class<?> c) {
             Module m = c.getModule();
             // add read edge and qualified export for the target module to access
             if (!target.canRead(m)) {
@@ -921,7 +836,7 @@
         /*
          * Ensure the given class is visible to the class loader.
          */
-        static void ensureVisible(ClassLoader ld, Class<?> c) {
+        private static void ensureVisible(ClassLoader ld, Class<?> c) {
             Class<?> type = null;
             try {
                 type = Class.forName(c.getName(), false, ld);
@@ -933,7 +848,7 @@
             }
         }
 
-        static Class<?> getElementType(Class<?> type) {
+        private static Class<?> getElementType(Class<?> type) {
             Class<?> e = type;
             while (e.isArray()) {
                 e = e.getComponentType();
@@ -941,7 +856,8 @@
             return e;
         }
 
-        private static final WeakHashMap<ClassLoader, Module> dynProxyModules = new WeakHashMap<>();
+        private static final ClassLoaderValue<Module> dynProxyModules =
+            new ClassLoaderValue<>();
         private static final AtomicInteger counter = new AtomicInteger();
 
         /*
@@ -950,12 +866,12 @@
          *
          * Each class loader will have one dynamic module.
          */
-        static Module getDynamicModule(ClassLoader loader) {
-            return dynProxyModules.computeIfAbsent(loader, ld -> {
+        private static Module getDynamicModule(ClassLoader loader) {
+            return dynProxyModules.computeIfAbsent(loader, (ld, clv) -> {
                 // create a dynamic module and setup module access
                 String mn = "jdk.proxy" + counter.incrementAndGet();
                 String pn = PROXY_PACKAGE_PREFIX + "." + mn;
-                Module m = Modules.defineModule(loader, mn, Collections.singleton(pn));
+                Module m = Modules.defineModule(ld, mn, Collections.singleton(pn));
                 Modules.addReads(m, Proxy.class.getModule());
                 // java.base to create proxy instance
                 Modules.addExports(m, pn, Object.class.getModule());
@@ -1062,40 +978,31 @@
                                           InvocationHandler h) {
         Objects.requireNonNull(h);
 
-        final List<Class<?>> intfs = List.of(interfaces);  // interfaces cloned
-        final SecurityManager sm = System.getSecurityManager();
-        final Class<?> caller = Reflection.getCallerClass();
-        if (sm != null) {
-            checkProxyAccess(caller, loader, intfs);
-        }
+        final Class<?> caller = System.getSecurityManager() == null
+                                    ? null
+                                    : Reflection.getCallerClass();
 
         /*
-         * Look up or generate the designated proxy class.
+         * Look up or generate the designated proxy class and its constructor.
          */
-        Class<?> cl = new ProxyBuilder(loader, intfs).build();
+        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
 
-        return newProxyInstance(cl, caller, h);
+        return newProxyInstance(caller, cons, h);
     }
 
-    private static Object newProxyInstance(Class<?> proxyClass, Class<?> caller, InvocationHandler h) {
+    private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
+                                           Constructor<?> cons,
+                                           InvocationHandler h) {
         /*
          * Invoke its constructor with the designated invocation handler.
          */
         try {
-            final SecurityManager sm = System.getSecurityManager();
-            if (sm != null) {
-                checkNewProxyPermission(caller, proxyClass);
+            if (caller != null) {
+                checkNewProxyPermission(caller, cons.getDeclaringClass());
             }
 
-            final Constructor<?> cons = proxyClass.getConstructor(constructorParams);
-            AccessController.doPrivileged(new PrivilegedAction<Void>() {
-                public Void run() {
-                    cons.setAccessible(true);
-                    return null;
-                }
-            });
             return cons.newInstance(new Object[]{h});
-        } catch (IllegalAccessException | InstantiationException | NoSuchMethodException e) {
+        } catch (IllegalAccessException | InstantiationException e) {
             throw new InternalError(e.toString(), e);
         } catch (InvocationTargetException e) {
             Throwable t = e.getCause();
@@ -1150,7 +1057,7 @@
      * @throws  NullPointerException if {@code cl} is {@code null}
      */
     public static boolean isProxyClass(Class<?> cl) {
-        return Proxy.class.isAssignableFrom(cl) && ProxyClassFactory.isProxyClass(cl);
+        return Proxy.class.isAssignableFrom(cl) && ProxyBuilder.isProxyClass(cl);
     }
 
     /**