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
8272564: Incorrect attribution of method invocations of Object methods on interfaces #5165
Conversation
|
Webrevs
|
if (trees.isAccessible(s, name)) { | ||
for (Element member : ct.getElements().getAllMembers(name)) { | ||
if (!trees.isAccessible(s, member, (DeclaredType) name.asType())) { | ||
trees.isAccessible(s, member, (DeclaredType) name.asType()); |
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.
Why is this statement necessary?
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.
Ooops, that is a leftover from testing (so that I can easily put a breakpoint on the call when false).
@lahodaj This change now passes all automated pre-integration checks. 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 596 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.
|
I think that this one deserves a CSR, plus please consider also running the JCK tests just to double check |
/csr needed |
@jddarcy has indicated that a compatibility and specification (CSR) request is needed for this pull request. |
question shouldn't field |
Thanks, I've started to file the CSR here, please take a look:
Not having interfaces "subclass" Thanks for the comments! |
yep I was also considering that it would be a big change, but seems like the right thing to do, I let it to your consideration |
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 implementation logic seems to be moving in the right direction.It is true that interface do not inherit members for Object
- but the JLS is set up in a strange way:
If an interface has no direct superinterface types, then the interface implicitly declares a public abstract member method m with signature s, return type r, and throws clause t corresponding to each public instance method m with signature s, return type r, and throws clause t declared in Object (§4.3.2), unless an abstract method with the same signature, same return type, and a compatible throws clause is explicitly declared by the interface.
This seems to suggest that resolution should return I.toString
even if I
does NOT declare a toString
method. That makes sense, after all, implementations of I
will always have some toString(). This is not what your fallback Object lookup in findMethod
does.
I also suggest running benchmarks, as the effect of these changes would be to generate invokeinterface where we used to have invokevirtual. VM should be smart enough to figure that one out, but better check.
@@ -1854,7 +1854,8 @@ private Symbol findMethod(Env<AttrContext> env, | |||
List<Type>[] itypes = (List<Type>[])new List[] { List.<Type>nil(), List.<Type>nil() }; | |||
|
|||
InterfaceLookupPhase iphase = InterfaceLookupPhase.ABSTRACT_OK; | |||
for (TypeSymbol s : superclasses(intype)) { | |||
boolean isInterface = site.tsym.isInterface(); | |||
for (TypeSymbol s : isInterface ? List.of(intype.tsym) : superclasses(intype)) { |
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 wonder if the code here is making it more difficult because we include the initial type into superclasses
. Maybe it could be worth to have an initial round where we just look into the initial type - followed by an (optional) round where we look at superclasses, followed by a round where we look at superinterfaces, followed by a fallback (optional) round where we look at Object.
Thanks for the comment @mcimadamore! In 16ed87f, I changed the code to generate
I also tried the benchmarks, but the results are more unclear there. I tried to split the loop for the current type and the superclasses, but in the end it didn't look very nice, so I skipped that for now. I can push the code somewhere, if desired. |
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 think we can go ahead for now. Thanks.
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 to me
/integrate |
Going to push as commit a5080ef.
Your commit was automatically rebased without conflicts. |
Consider these declarations:
There are two issues with the
toString
inherited fromI
intoJ
:-
Trees.isAccessible(..., I.toString, J)
will return false, becauseResolve.isAccessible
will return false, becauseResolve.notOverriddenIn
returns false, because theObject.toString
method is found as an implementation ofI.toString
in the context ofJ
. This is unfortunate, becauseElements.getAllMembers(J)
will returnI.toString
as a member, notObject.toString
, so any API client listing all members and then validating them usingTrees.isAccessible
will filtertoString
out. The proposed solution is to avoid using the methods fromjava.lang.Object
as implementations of interface methods inResolve.notOverriddenIn
. (Interfaces don't inherit fromj.l.Object
per my understanding of JLS.)-as a slightly less problematic case, consider:
I believe the second invocation should also preferably be a call to
I.toString()
, not a call toj.l.Object.toString()
. The reason for this behavior is that in javac, interfaces havej.l.Object
as a supertype, so method lookups over interfaces will look intoj.l.Object
before looking into the superinterfaces, and the concrete method will prevail over the super interface method found later. The proposal is, for interfaces, to only look intoj.l.Object
is a method is not found in the interface and its superinterfaces.Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/5165/head:pull/5165
$ git checkout pull/5165
Update a local copy of the PR:
$ git checkout pull/5165
$ git pull https://git.openjdk.java.net/jdk pull/5165/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 5165
View PR using the GUI difftool:
$ git pr show -t 5165
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/5165.diff