Skip to content
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

8225377: type annotations are not visible to javac plugins across compilation boundaries #16407

Closed
wants to merge 7 commits into from

Conversation

cushon
Copy link
Contributor

@cushon cushon commented Oct 27, 2023

Hello,

Please consider this fix for JDK-8225377: type annotations are not visible to javac plugins across compilation boundaries.


To provide some background context and motivation for the bug fix, consider an example like:

class B {
  @Nullable int f(int x) {
    return x;
  }
}

If @Nullable is a @Target(METHOD) annotation, an annotation processor can retrieve the annotation by from the ExecutableElement for f by calling getAnnotationMirrors(). This is true regardless of whether B is being compiled from source in the current compilation, or loaded from a class file.

If @Nullable is a @Target(TYPE_USE) annotation, an annotation processor should be able to retrieve the annotation by locating the ExecutableElement for f, and calling getReturnType().getAnnotationMirrors(). This works today if B is compiled from source, but (due to JDK-8225377) not if B is loaded from a class file.

This is a hurdle to migrating from existing declaration annotations to @Target(TYPE_USE) annotations for use-cases like @Nullable. For example: a dependency injection framework might use an annotation processor to generate code, and that code would want to know if a formal parameter type accepted null values, or if a method returned null values. That works today with declaration annotation definitions of @Nullable, and this fix will allow it work with TYPE_USE definitions of @Nullable in the future.


javac already reads type annotation information from Runtime{Visible,Invisible}TypeAnnotations attributes in class files, and ClassReader registers a completer that calls Symbol#setTypeAttributes to add the type annotations to the symbol's metadata. This change adds logic to associate those annotations with the corresponding type contained in the symbol.

Some notes on the approach:

  • There are some similarities between the logic being added and existing logic in TypeAnnotations, but TypeAnnotations operates on the AST, and is solving different problems. In the AST annotations are already adjacent to the type they are annotating, and in bytecode they have been separated out and the implementation has to interpret the annotation target and type path information to match the annotations back up with their types.

  • For initial test coverage, I have enabled test coverage for annotation processing of type annotations to also run the annotation processor on the pre-compiled class. There may be other type annotation tests that could be updated to exercise this change. Overall I think updating the existing extensive test coverage for type annotations avoids duplicating test inputs, and helps ensure both the type annotations for symbols compiled from source and loaded from bytecode match, and that they continue to match when there are changes in the future.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8225377: type annotations are not visible to javac plugins across compilation boundaries (Bug - P3)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/16407/head:pull/16407
$ git checkout pull/16407

Update a local copy of the PR:
$ git checkout pull/16407
$ git pull https://git.openjdk.org/jdk.git pull/16407/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 16407

View PR using the GUI difftool:
$ git pr show -t 16407

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/16407.diff

Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Oct 27, 2023

👋 Welcome back cushon! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk openjdk bot added the rfr Pull request is ready for review label Oct 27, 2023
@openjdk
Copy link

openjdk bot commented Oct 27, 2023

@cushon The following label will be automatically applied to this pull request:

  • compiler

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@mlbridge
Copy link

mlbridge bot commented Oct 27, 2023

* handle annotating the same type twice (with runtime-visible and
  -invisible annotations)
* don't throw an assertion on ill-formed type paths
* null-check ClassSymbol typarams
Copy link
Contributor

@vicente-romero-oracle vicente-romero-oracle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code in TypeAnnotationMapper looks good to me, actually simpler than expected. I just wonder if it should be placed inside of ClassReader, if you want to keep it separated please consider not exposing its API as static methods.

@@ -2261,6 +2261,7 @@ public void run() {
currentClassFile = classFile;
List<Attribute.TypeCompound> newList = deproxyTypeCompoundList(proxies);
sym.setTypeAttributes(newList.prependList(sym.getRawTypeAttributes()));
TypeAnnotationMapper.addTypeAnnotationsToSymbol(sym, newList);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the new TypeAnnotationsMapper should actually live inside of ClassReader

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tentatively gone ahead and moved it into ClassReader

@cushon
Copy link
Contributor Author

cushon commented Oct 30, 2023

Thanks for the review!

I just wonder if it should be placed inside of ClassReader, if you want to keep it separated please consider not exposing its API as static methods.

Moving it into ClassReader is fine with me. I initially thought it might end up requiring more logic than it did. I left it in a separate class because it's still a medium amount of logic and is fairly self-contained, but if you're preference is to move it into ClassReader I'm happy to proceed with that.

Just for my understanding, if it was a separate class would the preferred style be to register it in Context and have ClassReader create an instance of it?

@vicente-romero-oracle
Copy link
Contributor

Thanks for the review!

I just wonder if it should be placed inside of ClassReader, if you want to keep it separated please consider not exposing its API as static methods.

Moving it into ClassReader is fine with me. I initially thought it might end up requiring more logic than it did. I left it in a separate class because it's still a medium amount of logic and is fairly self-contained, but if you're preference is to move it into ClassReader I'm happy to proceed with that.

Just for my understanding, if it was a separate class would the preferred style be to register it in Context and have ClassReader create an instance of it?

if it was a separate instance I would prefer ClassReader to create an instance of it even it it is not registered in Context, but registering it in Context would be the "standard" approach I think

@cushon
Copy link
Contributor Author

cushon commented Oct 30, 2023

if it was a separate instance I would prefer ClassReader to create an instance of it even it it is not registered in Context, but registering it in Context would be the "standard" approach I think

Sounds good, thanks. I'm happy to implement whichever of these options you prefer. Do you prefer having the logic in ClassReader, or a separate class that ClassReader creates an instance of?

@vicente-romero-oracle
Copy link
Contributor

if it was a separate instance I would prefer ClassReader to create an instance of it even it it is not registered in Context, but registering it in Context would be the "standard" approach I think

Sounds good, thanks. I'm happy to implement whichever of these options you prefer. Do you prefer having the logic in ClassReader, or a separate class that ClassReader creates an instance of?

I'm OK with the last version, thanks

Copy link
Contributor

@vicente-romero-oracle vicente-romero-oracle left a 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

@openjdk
Copy link

openjdk bot commented Oct 30, 2023

@cushon 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:

8225377: type annotations are not visible to javac plugins across compilation boundaries

Reviewed-by: vromero

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 74 new commits pushed to the master branch:

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.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Oct 30, 2023
@cushon
Copy link
Contributor Author

cushon commented Nov 1, 2023

looks good to me

Thanks for the review!

I did some more validation of the change on Google's codebase, and everything looks good on that side.

I made one more small update for receiver parameters and added an additional test case. Receiver parameters are not currently filled in for classes loaded from the classpath. (That is potentially an issue even without type annotations being present, I filed JDK-8319196.)

I updated the logic here to fill in the receiver parameter type if METHOD_RECEIVER annotations are present:

diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
index f86a95e501c..d15d5ca2526 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
@@ -2333,8 +2333,9 @@ public Void visitMethodSymbol(Symbol.MethodSymbol s, Void unused) {
             }
             mt.thrown = thrown.toList();
             mt.restype = addTypeAnnotations(mt.restype, TargetType.METHOD_RETURN);
-            if (mt.recvtype != null) {
-                mt.recvtype = addTypeAnnotations(mt.recvtype, TargetType.METHOD_RECEIVER);
+            if (attributes.stream().anyMatch(a -> a.position.type == TargetType.METHOD_RECEIVER)) {
+                Assert.checkNull(mt.recvtype);
+                mt.recvtype = addTypeAnnotations(s.owner.type, TargetType.METHOD_RECEIVER);
             }
             return null;
         }
diff --git a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java
index af63575052c..18b41b4a80c 100644
--- a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java
+++ b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java
@@ -573,4 +573,10 @@ class Inner90<@TA(90) T> {
     @Test(posn=4, annoType = TB.class, expect = "100")
     class Inner100<T extends Inner100<@TB(100) T>> {
     }
+
+    // receiver parameters
+    class Inner110 {
+        @Test(posn=2, annoType = TA.class, expect = "110")
+        void f(@TA(110) Inner110 this) {}
+    }
 }

@cushon
Copy link
Contributor Author

cushon commented Nov 3, 2023

I made one more small update for receiver parameters and added an additional test case. Receiver parameters are not currently filled in for classes loaded from the classpath. (That is potentially an issue even without type annotations being present, I filed JDK-8319196.)

On second thought, I would like to defer that, I have switched back to the last version that was reviewed. The receiver parameter issue seems like a pre-existing problem that is doesn't just affect type annotations, I have proposed a separate fix for JDK-8319196.

@vicente-romero-oracle
Copy link
Contributor

I made one more small update for receiver parameters and added an additional test case. Receiver parameters are not currently filled in for classes loaded from the classpath. (That is potentially an issue even without type annotations being present, I filed JDK-8319196.)

On second thought, I would like to defer that, I have switched back to the last version that was reviewed. The receiver parameter issue seems like a pre-existing problem that is doesn't just affect type annotations, I have proposed a separate fix for JDK-8319196.

yep better to defer the receiver thing

@cushon
Copy link
Contributor Author

cushon commented Nov 3, 2023

/integrate

@openjdk
Copy link

openjdk bot commented Nov 3, 2023

Going to push as commit de6667c.
Since your change was applied there have been 79 commits pushed to the master branch:

  • 008ca2a: 8317620: Build JDK tools with ModuleMainClass attribute
  • 1a21c1a: 8318736: com/sun/jdi/JdwpOnThrowTest.java failed with "transport error 202: bind failed: Address already in use"
  • 81db172: 8318955: Add ReleaseIntArrayElements in Java_sun_awt_X11_XlibWrapper_SetBitmapShape XlbWrapper.c to early return
  • be01caf: 8319323: FFM: Harmonize the @throws tags in the javadocs
  • ec79ab4: 8319268: Build failure with GCC8.3.1 after 8313643
  • c788160: 8296240: Augment discussion of test tiers in doc/testing.md
  • ffaecd4: 8315364: Assert thread state invariant for JFR stack trace capture
  • 3b65b87: 8316028: Update FreeType to 2.13.2
  • 9dc40ba: 8319195: Move most tier 1 vector API regression tests to tier 3
  • f875163: 8318982: Improve Exceptions::special_exception
  • ... and 69 more: https://git.openjdk.org/jdk/compare/d2260146c9930002e430a874f2585d699dedc155...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Nov 3, 2023
@openjdk openjdk bot closed this Nov 3, 2023
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Nov 3, 2023
@openjdk
Copy link

openjdk bot commented Nov 3, 2023

@cushon Pushed as commit de6667c.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler [email protected] integrated Pull request has been integrated
Development

Successfully merging this pull request may close these issues.

2 participants