< prev index next >

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

Print this page

        

@@ -23,11 +23,10 @@
  * questions.
  */
 
 package java.lang.reflect;
 
-import java.lang.ref.WeakReference;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Deque;

@@ -37,14 +36,12 @@
 import java.util.LinkedList;
 import java.util.List;
 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;
 
 import jdk.internal.loader.BootLoader;
 import jdk.internal.module.Modules;

@@ -282,10 +279,17 @@
     /** parameter types of a proxy class constructor */
     private static final Class<?>[] constructorParams =
         { 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
      */
     protected InvocationHandler h;
 

@@ -359,18 +363,45 @@
     @CallerSensitive
     public static Class<?> getProxyClass(ClassLoader loader,
                                          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();
         }
 
-        return new ProxyBuilder(loader, intfs).build();
+    private static Constructor<?> getProxyConstructor(Class<?> caller, // null if no SecurityManager
+                                                      ClassLoader loader,
+                                                      Class<?>... interfaces)
+        throws IllegalArgumentException
+    {
+        // 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()
+            );
+        }
     }
 
     /*
      * Check permissions required to create a Proxy class.
      *

@@ -389,167 +420,42 @@
      * will throw IllegalAccessError when the generated proxy class is
      * being defined.
      */
     private static void checkProxyAccess(Class<?> caller,
                                          ClassLoader loader,
-                                         List<Class<?>> interfaces)
+                                         Class<?> ... interfaces)
     {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             ClassLoader ccl = caller.getClassLoader();
             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;
-        }
-    }
-
-    /**
-     * A function that maps an array of interfaces to an optimal key where
-     * Class objects representing interfaces are weakly referenced.
-     */
-    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);
-            }
+            ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
         }
     }
 
     /**
-     * A factory function that generates, defines and returns the proxy class
-     * given the ClassLoader and array of interfaces.
+     * 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 ProxyClassFactory {
+    private static final class ProxyBuilder {
         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
 
         // prefix for all proxy class names
         private static final String proxyClassNamePrefix = "$Proxy";
 
         // 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;
 
             /*

@@ -599,12 +505,15 @@
              * Generate the specified proxy class.
              */
             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                     proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
             try {
-                return UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length,
+                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
                  * proxy class generation code) there was some other
                  * invalid aspect of the arguments supplied to the proxy

@@ -614,39 +523,18 @@
                 throw new IllegalArgumentException(e.toString());
             }
         }
 
         /**
-         * 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 Boolean.TRUE.equals(
+                reverseProxyCache.sub(c).get(c.getClassLoader()));
         }
 
-        /**
-         * 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);
         }
 

@@ -683,29 +571,22 @@
                 public String run() {
                     return System.getProperty("jdk.proxy.debug", "");
                 }
             });
 
-        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);
         }
-    }
 
-    /**
-     * 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;
+        // ProxyBuilder instance members start here....
+
+        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");
             }
             if (interfaces.size() > 65535) {

@@ -721,30 +602,48 @@
             this.interfaces = interfaces;
             this.module = mapToModule(loader, interfaces, refTypes);
             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;
         }
 
         /**
          * Validate the given proxy interfaces and the given referenced types
          * are visible to the defining loader.
          *
          * @throws IllegalArgumentException if it violates the restrictions specified
          *         in {@link Proxy#newProxyInstance}
          */
-        static void validateProxyInterfaces(ClassLoader loader,
+        private static void validateProxyInterfaces(ClassLoader loader,
                                             List<Class<?>> interfaces,
                                             Set<Class<?>> refTypes)
         {
             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.size());
             for (Class<?> intf : interfaces) {

@@ -777,28 +676,29 @@
 
         /*
          * 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());
         }
 
         /*
          * 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);
         }
 
         /**
          * Returns the module that the generated proxy class belongs to.
          *

@@ -811,11 +711,13 @@
          * If all proxy interfaces are public and at least one in a non-exported
          * package, then the proxy class is in a dynamic module in a non-exported
          * 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) {
                 Module m = intf.getModule();
                 if (Modifier.isPublic(intf.getModifiers())) {

@@ -882,14 +784,13 @@
             // set up proxy class access to proxy interfaces and superinterfaces
             Deque<Class<?>> deque = new LinkedList<>(interfaces);
             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
                 for (Class<?> intf : c.getInterfaces()) {
                     deque.add(intf);

@@ -904,11 +805,11 @@
         }
 
         /*
          * 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)) {
                 Modules.addReads(target, m);
             }

@@ -919,11 +820,11 @@
         }
 
         /*
          * 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);
             } catch (ClassNotFoundException e) {
             }

@@ -931,33 +832,34 @@
                 throw new IllegalArgumentException(c.getName() +
                         " referenced from a method is not visible from class loader");
             }
         }
 
-        static Class<?> getElementType(Class<?> type) {
+        private static Class<?> getElementType(Class<?> type) {
             Class<?> e = type;
             while (e.isArray()) {
                 e = e.getComponentType();
             }
             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();
 
         /*
          * Define a dynamic module for the generated proxy classes in a non-exported package
          * named com.sun.proxy.$MODULE.
          *
          * 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());
                 return m;
             });

@@ -1060,44 +962,35 @@
     public static Object newProxyInstance(ClassLoader loader,
                                           Class<?>[] interfaces,
                                           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();
             if (t instanceof RuntimeException) {
                 throw (RuntimeException) t;

@@ -1148,11 +1041,11 @@
      * @return  {@code true} if the class is a proxy class and
      *          {@code false} otherwise
      * @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);
     }
 
     /**
      * Returns the invocation handler for the specified proxy instance.
      *
< prev index next >