-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8284942: Proxy building can just iterate superinterfaces once #8281
Conversation
👋 Welcome back liach! A progress list of the required criteria for merging this PR into |
Webrevs
|
@liach This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
Can anyone, such as Mandy, review this cleanup? |
String proxyPkg = null; // package to define proxy class in | ||
int accessFlags = Modifier.PUBLIC | Modifier.FINAL; | ||
boolean nonExported = false; | ||
private record ProxyContext(Module module, String pkg, boolean packagePrivate) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This represents the context for a proxy class. It seems ProxyClassContext
would be clearer. I suggest the context to have the accessFlags for a proxy class rather than the boolean whether it's package private. The constructor should check that it must be 0 or Modifier.PUBLIC
. FINAL
will be added to the access flags when it generates the proxy class.
private static Module mapToModule(ClassLoader loader, | ||
List<Class<?>> interfaces, | ||
Set<Class<?>> refTypes) { | ||
private static ProxyContext setupContext(ClassLoader loader, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps name this method proxyClassContext
that returns ProxyClassContext
.
} | ||
|
||
// 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); | ||
var context = getDynamicModuleContext(loader, nonExported); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to keep the getDynamicModule
method as creating a dynamic module of a given class loader is a clear function. The context can be created in this method body.
var targetModule = getDynamicModule(loader);
var pkgName = nonExported ? PROXY_PACKAGE_PREFIX + '.' + targetModule.getName()
: targetModule.getName();
var context = new ProxyClassContext(targetModule, pkgName, Modifier.PUBLIC);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good with a couple comments.
@@ -555,6 +531,7 @@ private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) { | |||
/* | |||
* Generate the specified proxy class. | |||
*/ | |||
accessFlags |= Modifier.FINAL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be inlined in the call to generateProxyClass
:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags | Modifier.FINAL);
if (!m.isNamed()) | ||
throw new InternalError("unnamed module: " + m); | ||
proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName() | ||
: m.getName(); | ||
} else if (proxyPkg.isEmpty() && m.isNamed()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the refactoring, it may be easier to understand to make this check for both public and non-public access (i.e. drop the else
) for clarity - a named module can't have unnamed package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a named module can't have unnamed package
Since this is mandated by the Java Language Specification 7.4.2, I am tempted to change the thrown exception to InternalError
, but I cannot find any restriction in the JVM Specification that prevents declaring a package-private superinterface in the unnamed package of a named module. If we can find a reference in the JVM Specification, I'm more than glad to refer to that in a comment and throw InternalError
instead.
if (proxyPkg.isEmpty() && m.isNamed()) { | ||
// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this patch, I think line 505-517 can turn into assert or internal error as you suggested. Probably can be moved to the ProxyClassContext
constructor too.
int accessFlags = Modifier.PUBLIC | Modifier.FINAL; | ||
boolean nonExported = false; | ||
private record ProxyClassContext(Module module, String pkg, int accessFlags) { | ||
private ProxyClassContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should validate the accessFlags
value as it must be 0 or PUBLIC
.
} | ||
|
||
if (!module.isOpen(pkg, Proxy.class.getModule())) { | ||
// Required for default method invocation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment true?
The module of the proxy class opens the package to java.base
if the proxy interface is non-public in a named module or if all proxy interfaces are public but a proxy interface is not unconditionally exported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original check and Modules.addOpen
calls were added in 8159476, when the invokeDefault
support was added.
See: 56b15fb#diff-c15cc774a95bac204c294b9ca8e20332c81904e506e16bb7d5a82d1c30cced17R667, or #313
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. That assert was added in PR #313 as a clarification to the current proxy implementation but not required by the InvocationHandle::invokeDefault
. It could have been added before the default method support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears to me that it's required by proxyClassLookup
to perform reflective access on the dynamic module or the module where the package-private interface is located.
String proxyPkg = null; // package to define proxy class in | ||
int accessFlags = Modifier.PUBLIC | Modifier.FINAL; | ||
boolean nonExported = false; | ||
private record ProxyClassContext(Module module, String pkg, int accessFlags) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private record ProxyClassContext(Module module, String pkg, int accessFlags) { | |
private record ProxyClassContext(Module module, String packageName, int accessFlags) { |
@mlchung Would you review again that I've updated per your suggestions? In addition, I've moved all proxy class context-related validation into the record compact constructor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Thanks.
@liach This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 49 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@mlchung) but any other Committer may sponsor as well. ➡️ To flag this PR as ready for integration with the above commit message, type |
/integrate |
@mlchung Would you mind sponsoring this patch? |
@liach This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
Would someone review or sponsor this mino improvement? |
Hello @liach, I see that Mandy has already reviewed and approved this PR and there hasn't been any change in this PR after that (which is a good thing). I'll run some tests internally today and if that goes fine, then I'll go ahead and sponsor this. |
Locally, I merged the latest master branch with this PR and then ran tier1, tier2 and tier3 tests. The tests passed without any related failures. I'll go ahead and sponsor this now. |
/sponsor |
Going to push as commit 0709a6a.
Your commit was automatically rebased without conflicts. |
Currently, in ProxyBuilder::mapToModule and ProxyBuilder::defineProxyClass, the interfaces are iterated twice. The two passes can be merged into one, yielding the whole proxy definition context (module, package, whether there's package-private interface) when determining the module.
Split from #8278. Helpful for moving proxies to hidden classes, but is a good cleanup on its own.
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/8281/head:pull/8281
$ git checkout pull/8281
Update a local copy of the PR:
$ git checkout pull/8281
$ git pull https://git.openjdk.java.net/jdk pull/8281/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 8281
View PR using the GUI difftool:
$ git pr show -t 8281
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/8281.diff