8284942: Proxy building can just iterate superinterfaces once

Reviewed-by: mchung
This commit is contained in:
liach 2022-06-29 14:22:48 +00:00 committed by Jaikiran Pai
parent 2961b7eede
commit 0709a6a1fb

View file

@ -495,67 +495,54 @@ public class Proxy implements java.io.Serializable {
private static final ClassLoaderValue<Boolean> reverseProxyCache = private static final ClassLoaderValue<Boolean> reverseProxyCache =
new ClassLoaderValue<>(); new ClassLoaderValue<>();
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) { private record ProxyClassContext(Module module, String packageName, int accessFlags) {
String proxyPkg = null; // package to define proxy class in private ProxyClassContext {
int accessFlags = Modifier.PUBLIC | Modifier.FINAL; if (module.isNamed()) {
boolean nonExported = false; 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
// package of a named module.
throw new InternalError("Unnamed package cannot be added to " + module);
}
/* if (!module.getDescriptor().packages().contains(packageName)) {
* Record the package of a non-public proxy interface so that the throw new InternalError(packageName + " not exist in " + module.getName());
* proxy class will be defined in the same package. Verify that }
* all non-public proxy interfaces are in the same package.
*/ if (!module.isOpen(packageName, Proxy.class.getModule())) {
for (Class<?> intf : interfaces) { // Required for default method invocation
int flags = intf.getModifiers(); throw new InternalError(packageName + " not open to " + Proxy.class.getModule());
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL; // non-public, final
String pkg = intf.getPackageName();
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
} }
} else { } else {
if (!intf.getModule().isExported(intf.getPackageName())) { if (Modifier.isPublic(accessFlags)) {
// module-private types // All proxy superinterfaces are public, must be in named dynamic module
nonExported = true; throw new InternalError("public proxy in unnamed module: " + module);
} }
} }
}
if (proxyPkg == null) { if ((accessFlags & ~Modifier.PUBLIC) != 0) {
// all proxy interfaces are public and exported throw new InternalError("proxy access flags must be Modifier.PUBLIC or 0");
if (!m.isNamed())
throw new InternalError("unnamed module: " + m);
proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName()
: m.getName();
} else if (proxyPkg.isEmpty() && m.isNamed()) {
throw new IllegalArgumentException(
"Unnamed package cannot be added to " + m);
}
if (m.isNamed()) {
if (!m.getDescriptor().packages().contains(proxyPkg)) {
throw new InternalError(proxyPkg + " not exist in " + m.getName());
} }
} }
}
private static Class<?> defineProxyClass(ProxyClassContext context, List<Class<?>> interfaces) {
/* /*
* Choose a name for the proxy class to generate. * Choose a name for the proxy class to generate.
*/ */
long num = nextUniqueNumber.getAndIncrement(); long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg.isEmpty() String proxyName = context.packageName().isEmpty()
? proxyClassNamePrefix + num ? proxyClassNamePrefix + num
: proxyPkg + "." + proxyClassNamePrefix + num; : context.packageName() + "." + proxyClassNamePrefix + num;
ClassLoader loader = getLoader(m); ClassLoader loader = getLoader(context.module());
trace(proxyName, m, loader, interfaces); trace(proxyName, context.module(), loader, interfaces);
/* /*
* Generate the specified proxy class. * Generate the specified proxy class.
*/ */
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags); byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces,
context.accessFlags() | Modifier.FINAL);
try { try {
Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile, Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
null, "__dynamic_proxy__"); null, "__dynamic_proxy__");
@ -575,7 +562,7 @@ public class Proxy implements java.io.Serializable {
/** /**
* Test if given class is a class defined by * Test if given class is a class defined by
* {@link #defineProxyClass(Module, List)} * {@link #defineProxyClass(ProxyClassContext, List)}
*/ */
static boolean isProxyClass(Class<?> c) { static boolean isProxyClass(Class<?> c) {
return Objects.equals(reverseProxyCache.sub(c).get(c.getClassLoader()), return Objects.equals(reverseProxyCache.sub(c).get(c.getClassLoader()),
@ -631,7 +618,7 @@ public class Proxy implements java.io.Serializable {
// ProxyBuilder instance members start here.... // ProxyBuilder instance members start here....
private final List<Class<?>> interfaces; private final List<Class<?>> interfaces;
private final Module module; private final ProxyClassContext context;
ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) { ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
if (!VM.isModuleSystemInited()) { if (!VM.isModuleSystemInited()) {
throw new InternalError("Proxy is not supported until " throw new InternalError("Proxy is not supported until "
@ -648,8 +635,8 @@ public class Proxy implements java.io.Serializable {
validateProxyInterfaces(loader, interfaces, refTypes); validateProxyInterfaces(loader, interfaces, refTypes);
this.interfaces = interfaces; this.interfaces = interfaces;
this.module = mapToModule(loader, interfaces, refTypes); this.context = proxyClassContext(loader, interfaces, refTypes);
assert getLoader(module) == loader; assert getLoader(context.module()) == loader;
} }
ProxyBuilder(ClassLoader loader, Class<?> intf) { ProxyBuilder(ClassLoader loader, Class<?> intf) {
@ -667,8 +654,7 @@ public class Proxy implements java.io.Serializable {
*/ */
@SuppressWarnings("removal") @SuppressWarnings("removal")
Constructor<?> build() { Constructor<?> build() {
Class<?> proxyClass = defineProxyClass(module, interfaces); Class<?> proxyClass = defineProxyClass(context, interfaces);
assert !module.isNamed() || module.isOpen(proxyClass.getPackageName(), Proxy.class.getModule());
final Constructor<?> cons; final Constructor<?> cons;
try { try {
@ -768,10 +754,11 @@ public class Proxy implements java.io.Serializable {
} }
/** /**
* Returns the module that the generated proxy class belongs to. * Returns the context for the generated proxy class, including the
* module and the package it belongs to and whether it is package-private.
* *
* If any of proxy interface is package-private, then the proxy class * If any of proxy interface is package-private, then the proxy class
* is in the same module of the package-private interface. * is in the same package and module as the package-private interface.
* *
* If all proxy interfaces are public and in exported packages, * If all proxy interfaces are public and in exported packages,
* then the proxy class is in a dynamic module in an unconditionally * then the proxy class is in a dynamic module in an unconditionally
@ -785,14 +772,21 @@ public class Proxy implements java.io.Serializable {
* *
* Reads edge and qualified exports are added for dynamic module to access. * Reads edge and qualified exports are added for dynamic module to access.
*/ */
private static Module mapToModule(ClassLoader loader, private static ProxyClassContext proxyClassContext(ClassLoader loader,
List<Class<?>> interfaces, List<Class<?>> interfaces,
Set<Class<?>> refTypes) { Set<Class<?>> refTypes) {
Map<Class<?>, Module> packagePrivateTypes = new HashMap<>(); Map<Class<?>, Module> packagePrivateTypes = new HashMap<>();
boolean nonExported = false;
for (Class<?> intf : interfaces) { for (Class<?> intf : interfaces) {
Module m = intf.getModule(); Module m = intf.getModule();
if (!Modifier.isPublic(intf.getModifiers())) { if (!Modifier.isPublic(intf.getModifiers())) {
packagePrivateTypes.put(intf, m); packagePrivateTypes.put(intf, m);
} else {
if (!intf.getModule().isExported(intf.getPackageName())) {
// module-private types
nonExported = true;
}
} }
} }
@ -838,7 +832,7 @@ public class Proxy implements java.io.Serializable {
Modules.addOpens(targetModule, targetPackageName, Proxy.class.getModule()); Modules.addOpens(targetModule, targetPackageName, Proxy.class.getModule());
} }
// return the module of the package-private interface // return the module of the package-private interface
return targetModule; return new ProxyClassContext(targetModule, targetPackageName, 0);
} }
// All proxy interfaces are public. So maps to a dynamic proxy module // All proxy interfaces are public. So maps to a dynamic proxy module
@ -852,7 +846,10 @@ public class Proxy implements java.io.Serializable {
for (Class<?> c : types) { for (Class<?> c : types) {
ensureAccess(targetModule, c); ensureAccess(targetModule, c);
} }
return targetModule;
var pkgName = nonExported ? PROXY_PACKAGE_PREFIX + '.' + targetModule.getName()
: targetModule.getName();
return new ProxyClassContext(targetModule, pkgName, Modifier.PUBLIC);
} }
/* /*