JDK 13 RFR of JDK-8164819: Make javac's toString() on annotation objects consistent with core reflection

Joe Darcy joe.darcy at oracle.com
Mon Jun 3 20:38:51 UTC 2019


Hello,

Please review the webrev of changes for

     JDK-8164819: Make javac's toString() on annotation objects 
consistent with core reflection
     http://cr.openjdk.java.net/~darcy/8164819.2/

Some background, several years ago in JDK 9, the toString output of 
annotations was changed to be usable as the source form of annotations 
(JDK-8162817: "Annotation toString output not reusable for source input").

Separate from the core reflection implementation of annotations, there 
are two other representations of that information available through 
annotation processing in javac from the 
javax.lang.model.AnnotatedConstruct interface, as AnnotationMirror 
objects and as  compile-time implementation of annotations in javac. 
(Annotations are implemented as Proxies of the interface of the 
annotation type both at runtime in core reflection and in javac.)

The two intertwined toString implementations of annotation information 
in javac are distinct from the one core reflection and differ in how 
some information is presented. The changes discussed below move the 
javac and core reflection implementations very close to one another by 
making changes in each. The actual code changes are small; the lines of 
test changes are large, but mostly mechanical.

The changes to the javac output are made in:
src/jdk.compiler/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java

The changes to javac's output are:

* The string for of class literals, either stand-alone or in an array 
now properly end in ".class".
* For an enum constant, just the constant name is emitted rather than 
the full class name followed by the enum constant name. This shorter 
form is also used by javadoc.

The changes to the core reflection output are made in:
src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java

The changes to core reflection output are:

* Eliding "value=" when it is not necessary, logic already present for 
this in the javac implementations.
* Changing the formatting of byte values to follow the existing javac 
convention, "(byte)0x1a"
* Adopting the same quoting policy for char and string values as javac. 
This was accomplished by copying a few small methods from the javac 
internals; I don't think it is worthwhile to go through the contortions 
that would be needed to share this small bit of rarely changing code 
across these modules.
* Unconditionally append a trailing "L" to long values, as done by javac.

The core reflection changes are directly exercised by updates to:

     test/jdk/java/lang/annotation/AnnotationToStringTest.java

A new compile-time test for javac's implementations using (nearly) the 
same test inputs is added as

test/langtools/tools/javac/processing/model/element/AnnotationToStringTest.java

While technically possible, I don't think it would be worth the 
complexity to try to share the javac and core reflection testing driven 
by the same test vectors in a single test file. In particular, if 
changes to one of core reflection and javac altered the annotation 
output, having tests in each area would help flag the change sooner.

The diff of the two flavors of AnnotationToStringTest.java is below and 
shows that the code to extract the annotations values differs between 
the javac and runtime API variants, but that the test vectors are the same.

The other test updates, especially in the directly 
test/langtools/tools/javac/processing/model/element/repeatingAnnotations 
are to change the tests for the new output, especially for when "value=" 
it is not needed. I'm still working on the exact updates needed for

     test/langtools/tools/javac/sym/ElementStructureTest.java

but I wanted to get the remainder of the work out for review.

Thanks,

-Joe

Diff of test files:

diff test/jdk/java/lang/annotation/AnnotationToStringTest.java 
test/langtools/tools/javac/processing/model/element/AnnotationToStringTest.java
26c26
<  * @bug 8162817 8168921
---
 >  * @bug 8164819
27a28,30
 >  * @library /tools/javac/lib
 >  * @build   JavacTestingAbstractProcessor AnnotationToStringTest
 >  * @compile -processor AnnotationToStringTest -proc:only 
AnnotationToStringTest.java
30,31c33,34
< // See also the sibling compile-time test
< // 
test/langtools/tools/javac/processing/model/element/AnnotationToStringTest.java
---
 > // See also the sibling core reflection test
 > // test/jdk/java/lang/annotation/AnnotationToStringTest.java
35a39,42
 > import javax.annotation.processing.*;
 > import javax.lang.model.AnnotatedConstruct;
 > import javax.lang.model.element.*;
 > import javax.lang.model.util.*;
40a48,55
 >  *
 >  * Two flavors of comparison are made:
 >  *
 >  * 1) Against the AnnotationMirror value from getAnnotationMirrors()
 >  *
 >  * 2) Against the *Annotation* from getAnnotation(Class<A>)
 >  *
 >  * These have separate but related implementations.
41a57,60
 > public class AnnotationToStringTest extends 
JavacTestingAbstractProcessor {
 >     public boolean process(Set<? extends TypeElement> annotations,
 >                            RoundEnvironment roundEnv) {
 >         if (!roundEnv.processingOver()) {
43,45c62,93
< public class AnnotationToStringTest {
<     public static void main(String... args) throws Exception {
<         int failures = 0;
---
 >             int failures = 0;
 >
 >             TypeElement primHostElt =
 > 
Objects.requireNonNull(elements.getTypeElement("AnnotationToStringTest.PrimHost"));
 >
 >             List<? extends AnnotationMirror> annotMirrors = 
primHostElt.getAnnotationMirrors();
 >
 >             String expectedString = 
primHostElt.getAnnotation(MostlyPrimitive.class).toString();
 >
 >             failures += check(expectedString,
 > primHostElt.getAnnotation(ExpectedString.class).value());
 >
 >             failures += check(expectedString,
 > retrieveAnnotationMirrorAsString(primHostElt,
 >              "MostlyPrimitive"));
 >             failures += classyTest();
 >             failures += arrayAnnotationTest();
 >
 >             if (failures > 0)
 >                 throw new RuntimeException(failures + " failures");
 >         }
 >         return true;
 >     }
 >
 >     /**
 >      * Examine annotation mirrors, find the one that matches
 >      * annotationName, and return its toString value.
 >      */
 >     private String 
retrieveAnnotationMirrorAsString(AnnotatedConstruct annotated,
 >   String annotationName) {
 >         return retrieveAnnotationMirror(annotated, 
annotationName).toString();
 >     }
47,50c95,106
<         failures += 
check(PrimHost.class.getAnnotation(ExpectedString.class).value(),
< PrimHost.class.getAnnotation(MostlyPrimitive.class).toString());
<         failures += classyTest();
<         failures += arrayAnnotationTest();
---
 >     private String retrieveAnnotationMirrorValue(AnnotatedConstruct 
annotated,
 >  String annotationName) {
 >         AnnotationMirror annotationMirror =
 >             retrieveAnnotationMirror(annotated, annotationName);
 >         for (var entry : 
annotationMirror.getElementValues().entrySet()) {
 >             if (entry.getKey().getSimpleName().contentEquals("value")) {
 >                 return entry.getValue().toString();
 >             }
 >         }
 >         throw new RuntimeException("Annotation value() method not 
found: " +
 >  annotationMirror.toString());
 >     }
52,53c108,119
<         if (failures > 0)
<             throw new RuntimeException(failures + " failures");
---
 >     private AnnotationMirror 
retrieveAnnotationMirror(AnnotatedConstruct annotated,
 >     String annotationName) {
 >         for (AnnotationMirror annotationMirror : 
annotated.getAnnotationMirrors()) {
 > System.out.println(annotationMirror.getAnnotationType());
 >             if (annotationMirror
 >                 .getAnnotationType()
 >                 .toString()
 >                 .equals(annotationName) ) {
 >                 return annotationMirror;
 >             }
 >         }
 >         throw new RuntimeException("Annotation " + annotationName + " 
not found.");
104c170
<     private static int classyTest() {
---
 >     private int classyTest() {
106c172,177
<         for (Field f : AnnotationHost.class.getFields()) {
---
 >
 >         TypeElement annotationHostElt =
 > 
Objects.requireNonNull(elements.getTypeElement("AnnotationToStringTest.AnnotationHost"));
 >
 >         for (VariableElement f : 
ElementFilter.fieldsIn(annotationHostElt.getEnclosedElements())) {
 >             String expected = 
f.getAnnotation(ExpectedString.class).value();
107a179
 >
109,110c181,184
<             failures += 
check(f.getAnnotation(ExpectedString.class).value(),
<                               a.toString());
---
 >             failures += check(expected, a.toString());
 >
 >             failures += check(expected,
 > retrieveAnnotationMirrorAsString(f, "Classy") );
151c225
<     private static int arrayAnnotationTest() {
---
 >     private int arrayAnnotationTest() {
153,157c227,251
<         for (Field f : ArrayAnnotationHost.class.getFields()) {
<             Annotation[] annotations = f.getAnnotations();
<             System.out.println(annotations[1]);
<             failures += check(((ExpectedString)annotations[0]).value(),
< annotations[1].toString());
---
 >
 >         TypeElement arrayAnnotationHostElt =
 >             Objects.requireNonNull(elements
 >  .getTypeElement("AnnotationToStringTest.ArrayAnnotationHost"));
 >
 >         for (VariableElement f :
 >  ElementFilter.fieldsIn(arrayAnnotationHostElt.getEnclosedElements())) {
 >             var annotations = f.getAnnotationMirrors();
 >             // String expected = retrieveAnnotationMirrorValue(f, 
"ExpectedString");
 >             String expected = 
f.getAnnotation(ExpectedString.class).value();
 >
 >             // Problem with
 >             // Need a de-quote method...
 >             // expected = expected.substring(1, expected.length() - 1);
 >
 >               failures +=
 >                   check(expected,
 > annotations.get(1).toString());
 >
 >             // Get the array-valued annotation as an annotation
 >               failures +=
 >                   check(expected,
 > retrieveAnnotationMirrorAsString(f,
 >        annotations.get(1)
 >        .getAnnotationType().toString()));
176a271
 >



More information about the core-libs-dev mailing list