< prev index next >

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

Print this page
*** 31,10 ***
--- 31,11 ---
  import java.lang.invoke.WrongMethodTypeException;
  import java.lang.module.ModuleDescriptor;
  import java.security.AccessController;
  import java.security.PrivilegedAction;
  import java.util.ArrayDeque;
+ import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.Deque;
  import java.util.HashMap;
  import java.util.HashSet;

*** 50,10 ***
--- 51,11 ---
  
  import jdk.internal.access.JavaLangAccess;
  import jdk.internal.access.SharedSecrets;
  import jdk.internal.module.Modules;
  import jdk.internal.misc.VM;
+ import jdk.internal.misc.CDS;
  import jdk.internal.reflect.CallerSensitive;
  import jdk.internal.reflect.Reflection;
  import jdk.internal.loader.ClassLoaderValue;
  import jdk.internal.vm.annotation.Stable;
  import sun.reflect.misc.ReflectUtil;

*** 438,10 ***
--- 440,17 ---
                  (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
              );
          }
      }
  
+     /**
+      * Called from VM native code during dump time.
+      */
+     private static void initCacheForCDS(ClassLoader platformLoader, ClassLoader appLoader) {
+         ProxyBuilder.initCacheForCDS(platformLoader, appLoader);
+     }
+ 
      /*
       * Check permissions required to create a Proxy class.
       *
       * To define a proxy class, it performs the access checks as in
       * Class.forName (VM will invoke ClassLoader.checkPackageAccess):

*** 481,21 ***
       * in which the proxy class will be defined.
       */
      private static final class ProxyBuilder {
          private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
  
          // 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 record ProxyClassContext(Module module, String packageName, int accessFlags) {
              private ProxyClassContext {
                  if (module.isNamed()) {
                      if (packageName.isEmpty()) {
                          // Per JLS 7.4.2, unnamed package can only exist in unnamed modules.
                          // This means a package-private superinterface exist in the unnamed
--- 490,34 ---
       * in which the proxy class will be defined.
       */
      private static final class ProxyBuilder {
          private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
  
+         private static String makeProxyClassNamePrefix() {
+             // Allow unique proxy names to be used across CDS dump time and app run time.
+             if (CDS.isDumpingArchive()) {
+                 if (CDS.isUsingArchive()) {
+                     return "$Proxy0100"; // CDS dynamic dump
+                 } else {
+                     return "$Proxy0010"; // CDS static dump
+                 }
+             } else {
+                 return "$Proxy";
+             }
+         }
+ 
          // prefix for all proxy class names
!         private static final String proxyClassNamePrefix = makeProxyClassNamePrefix();
  
          // 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 record ProxyClassContext(Module module, String packageName, int accessFlags, boolean isDynamicModule) {
              private ProxyClassContext {
                  if (module.isNamed()) {
                      if (packageName.isEmpty()) {
                          // Per JLS 7.4.2, unnamed package can only exist in unnamed modules.
                          // This means a package-private superinterface exist in the unnamed

*** 526,23 ***
  
          private static Class<?> defineProxyClass(ProxyClassContext context, List<Class<?>> interfaces) {
              /*
               * Choose a name for the proxy class to generate.
               */
              long num = nextUniqueNumber.getAndIncrement();
!             String proxyName = context.packageName().isEmpty()
-                                     ? proxyClassNamePrefix + num
-                                     : context.packageName() + "." + proxyClassNamePrefix + num;
  
-             ClassLoader loader = getLoader(context.module());
              trace(proxyName, context.module(), loader, interfaces);
  
              /*
               * Generate the specified proxy class.
               */
!             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces,
-                                                                       context.accessFlags() | Modifier.FINAL);
              try {
                  Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                                null, "__dynamic_proxy__");
                  reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                  return pc;
--- 548,37 ---
  
          private static Class<?> defineProxyClass(ProxyClassContext context, List<Class<?>> interfaces) {
              /*
               * Choose a name for the proxy class to generate.
               */
+             String packagePrefix = context.packageName().isEmpty()
+                                     ? proxyClassNamePrefix
+                                     : context.packageName() + "." + proxyClassNamePrefix;
+             ClassLoader loader = getLoader(context.module());
+             int accessFlags = context.accessFlags() | Modifier.FINAL;
+ 
+             if (archivedData != null) {
+                 Class<?> pc = archivedData.getArchivedProxyClass(loader, packagePrefix, interfaces);
+                 if (pc != null) {
+                     reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
+                     return pc;
+                 }
+             }
+ 
              long num = nextUniqueNumber.getAndIncrement();
!             String proxyName = packagePrefix + num;
  
              trace(proxyName, context.module(), loader, interfaces);
  
+             if (CDS.isLoggingDynamicProxies() && context.isDynamicModule()) {
+                 CDS.logDynamicProxy(loader, proxyName, interfaces.toArray(new Class<?>[0]), accessFlags);
+             }
+ 
              /*
               * Generate the specified proxy class.
               */
!             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
              try {
                  Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                                null, "__dynamic_proxy__");
                  reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                  return pc;

*** 556,10 ***
--- 592,27 ---
                   */
                  throw new IllegalArgumentException(e.toString());
              }
          }
  
+         /**
+          * Called from VM native code to define a proxy class to be stored in archivedData.
+          */
+         private static Class<?> defineProxyClassForCDS(ClassLoader loader, String proxyName, Class<?>[] interfaces,
+                                                        int accessFlags) {
+             ArrayList<Class<?>> list = new ArrayList<>();
+             for (Object o : interfaces) {
+                 list.add((Class<?>)o);
+             }
+ 
+             ProxyBuilder builder = new ProxyBuilder(loader, list);
+             Constructor<?> cons = builder.build();
+             Class<?> proxyClass = cons.getDeclaringClass();
+             archivedData.putArchivedProxyClass(loader, proxyName, list, proxyClass);
+             return proxyClass;
+         }
+ 
          /**
           * Test if given class is a class defined by
           * {@link #defineProxyClass(ProxyClassContext, List)}
           */
          static boolean isProxyClass(Class<?> c) {

*** 829,11 ***
                  // opens the package of the non-public proxy class for java.base to access
                  if (targetModule.isNamed()) {
                      Modules.addOpens(targetModule, targetPackageName, Proxy.class.getModule());
                  }
                  // return the module of the package-private interface
!                 return new ProxyClassContext(targetModule, targetPackageName, 0);
              }
  
              // All proxy interfaces are public.  So maps to a dynamic proxy module
              // and add reads edge and qualified exports, if necessary
              Module targetModule = getDynamicModule(loader);
--- 882,11 ---
                  // opens the package of the non-public proxy class for java.base to access
                  if (targetModule.isNamed()) {
                      Modules.addOpens(targetModule, targetPackageName, Proxy.class.getModule());
                  }
                  // return the module of the package-private interface
!                 return new ProxyClassContext(targetModule, targetPackageName, 0, false);
              }
  
              // All proxy interfaces are public.  So maps to a dynamic proxy module
              // and add reads edge and qualified exports, if necessary
              Module targetModule = getDynamicModule(loader);

*** 846,11 ***
                  ensureAccess(targetModule, c);
              }
  
              var pkgName = nonExported ? PROXY_PACKAGE_PREFIX + '.' + targetModule.getName()
                                        : targetModule.getName();
!             return new ProxyClassContext(targetModule, pkgName, Modifier.PUBLIC);
          }
  
          /*
           * Ensure the given module can access the given class.
           */
--- 899,11 ---
                  ensureAccess(targetModule, c);
              }
  
              var pkgName = nonExported ? PROXY_PACKAGE_PREFIX + '.' + targetModule.getName()
                                        : targetModule.getName();
!             return new ProxyClassContext(targetModule, pkgName, Modifier.PUBLIC, true);
          }
  
          /*
           * Ensure the given module can access the given class.
           */

*** 903,26 ***
           * Each class loader will have one dynamic module.
           */
          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;
                  ModuleDescriptor descriptor =
                          ModuleDescriptor.newModule(mn, Set.of(SYNTHETIC))
                                          .packages(Set.of(pn, mn))
                                          .exports(mn)
                                          .build();
                  Module m = Modules.defineModule(ld, descriptor, null);
!                 Modules.addReads(m, Proxy.class.getModule());
!                 Modules.addExports(m, mn);
!                 // java.base to create proxy instance and access its Lookup instance
!                 Modules.addOpens(m, pn, Proxy.class.getModule());
!                 Modules.addOpens(m, mn, Proxy.class.getModule());
                  return m;
              });
          }
      }
  
      /**
       * Returns a proxy instance for the specified interfaces
       * that dispatches method invocations to the specified invocation
--- 956,174 ---
           * Each class loader will have one dynamic module.
           */
          private static Module getDynamicModule(ClassLoader loader) {
              return dynProxyModules.computeIfAbsent(loader, (ld, clv) -> {
                  // create a dynamic module and setup module access
!                 int num = counter.incrementAndGet();
+                 String mn = "jdk.proxy" + num;
                  String pn = PROXY_PACKAGE_PREFIX + "." + mn;
                  ModuleDescriptor descriptor =
                          ModuleDescriptor.newModule(mn, Set.of(SYNTHETIC))
                                          .packages(Set.of(pn, mn))
                                          .exports(mn)
                                          .build();
                  Module m = Modules.defineModule(ld, descriptor, null);
!                 openDynamicModule(m);
! 
!                 if (CDS.isDumpingHeap() && archivedData != null) {
!                     archivedData.recordModule(loader, m, num);
!                 }
                  return m;
              });
          }
+ 
+         private static void openDynamicModule(Module m) {
+             String mn = m.getName();
+             String pn = PROXY_PACKAGE_PREFIX + "." + mn;
+             Modules.addReads(m, Proxy.class.getModule());
+             Modules.addExports(m, mn);
+             // java.base to create proxy instance and access its Lookup instance
+             Modules.addOpens(m, pn, Proxy.class.getModule());
+             Modules.addOpens(m, mn, Proxy.class.getModule());
+         }
+ 
+         static class ArchivedData {
+             static class InterfacesKey {
+                 Class<?>[] intfsArray;
+                 InterfacesKey(List<Class<?>> intfs) {
+                     intfsArray = new Class<?>[intfs.size()];
+                     for (int i = 0; i < intfs.size(); i++) {
+                         intfsArray[i] = intfs.get(i);
+                     }
+                 }
+                 @Override
+                 public int hashCode() {
+                     return Arrays.hashCode(intfsArray);
+                 }
+                 @Override
+                 public boolean equals(Object other) {
+                     if (other instanceof InterfacesKey) {
+                         InterfacesKey o = (InterfacesKey)other;
+                         int len = intfsArray.length;
+                         if (len != o.intfsArray.length) {
+                             return false;
+                         }
+                         Class<?>[] oa = o.intfsArray;
+                         for (int i = 0; i < len; i++) {
+                             if (intfsArray[i] != oa[i]) {
+                                 return false;
+                             }
+                         }
+                         return true;
+                     } else {
+                         return false;
+                     }
+                 }
+             }
+ 
+             ClassLoader platformLoader;
+             ClassLoader appLoader;
+             HashMap<InterfacesKey,Class<?>> bootCache = new HashMap<>();
+             HashMap<InterfacesKey,Class<?>> platformCache = new HashMap<>();
+             HashMap<InterfacesKey,Class<?>> appCache = new HashMap<>();
+ 
+             Module bootModule;
+             Module platformModule;
+             Module appModule;
+             int maxNum;
+ 
+             ArchivedData(ClassLoader plat, ClassLoader app) {
+                 platformLoader = plat;
+                 appLoader = app;
+             }
+ 
+             HashMap<InterfacesKey,Class<?>> cacheForLoader(ClassLoader loader) {
+                 if (loader == null) {
+                     return bootCache;
+                 } else if (loader == platformLoader) {
+                     return platformCache;
+                 } else if (loader == appLoader) {
+                     return appCache;
+                 } else {
+                     return null;
+                 }
+             }
+ 
+             void recordModule(ClassLoader loader, Module m, int num) {
+                 if (loader == null) {
+                     bootModule = m;
+                 } else if (loader == platformLoader) {
+                     platformModule = m;
+                 } else if (loader == appLoader) {
+                     appModule = m;
+                 } else {
+                     throw new UnsupportedOperationException("Class loader " + loader + " is not supported");
+                 }
+                 if (maxNum < num) {
+                     maxNum = num;
+                 }
+             }
+ 
+             void restore() {
+                 // The info for addReads/addExports/addOpens are maintained solely inside the VM.
+                 // CDS currently doesn't properly archive such info for the dynamically generated modules,
+                 // so we have to recreate them at runtime.
+                 //
+                 // TODO --  consider improving CDS to archive the above info, so we can avoid calling openDynamicModule()
+                 if (bootModule != null) {
+                     Module last = dynProxyModules.putIfAbsent(null, bootModule);
+                     assert last == null;
+                     openDynamicModule(bootModule);
+                 }
+                 if (platformModule != null) {
+                     Module last = dynProxyModules.putIfAbsent(platformLoader, platformModule);
+                     assert last == null;
+                     openDynamicModule(platformModule);
+                 }
+                 if (appModule != null) {
+                     Module last = dynProxyModules.putIfAbsent(appLoader, appModule);
+                     assert last == null;
+                     openDynamicModule(appModule);
+                 }
+ 
+                 while (maxNum > counter.get()) {
+                     counter.incrementAndGet();
+                 }
+             }
+ 
+ 
+             Class<?> getArchivedProxyClass(ClassLoader loader, String proxyPrefix, List<Class<?>> interfaces) {
+                 HashMap<InterfacesKey,Class<?>> cache = cacheForLoader(loader);
+                 if (cache != null && cache.size() > 0) {
+                     InterfacesKey key = new InterfacesKey(interfaces);
+                     return cache.get(key);
+                 } else {
+                     return null;
+                 }
+             }
+ 
+             void putArchivedProxyClass(ClassLoader loader, String proxyName, List<Class<?>> interfaces, Class<?> cls) {
+                 HashMap<InterfacesKey,Class<?>> cache = cacheForLoader(loader);
+                 if (cache != null) {
+                     InterfacesKey key = new InterfacesKey(interfaces);
+                     cache.put(key, cls);
+                 }
+             }
+         }
+ 
+         static ArchivedData archivedData;
+ 
+         static {
+             CDS.initializeFromArchive(ProxyBuilder.class);
+             if (archivedData != null) {
+                 archivedData.restore();
+             }
+         }
+ 
+         private static void initCacheForCDS(ClassLoader platformLoader, ClassLoader appLoader) {
+             archivedData = new ArchivedData(platformLoader, appLoader);
+         }
      }
  
      /**
       * Returns a proxy instance for the specified interfaces
       * that dispatches method invocations to the specified invocation
< prev index next >