8323684: TypeMirror.{getAnnotationsByType, getAnnotation} return uninformative results

Reviewed-by: jjg
This commit is contained in:
Joe Darcy 2024-01-18 19:04:26 +00:00
parent 5c874c19cb
commit a6c0b10704
2 changed files with 132 additions and 18 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -465,20 +465,6 @@ public abstract class Type extends AnnoConstruct implements TypeMirror, PoolCons
return getMetadata(TypeMetadata.Annotations.class, Annotations::annotations, List.nil());
}
@Override @DefinedBy(Api.LANGUAGE_MODEL)
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
return null;
}
@Override @DefinedBy(Api.LANGUAGE_MODEL)
public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
@SuppressWarnings("unchecked")
A[] tmp = (A[]) java.lang.reflect.Array.newInstance(annotationType, 0);
return tmp;
}
/** Return the base types of a list of types.
*/
public static List<Type> baseTypes(List<Type> ts) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,7 @@
/*
* @test
* @bug 8013852 8031744 8225377
* @bug 8013852 8031744 8225377 8323684
* @summary Annotations on types
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
@ -42,10 +42,13 @@ import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.AbstractMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
@ -86,6 +89,23 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
DPrinter dprinter;
PrintWriter out;
boolean verbose = true;
// Use a compile-time mapping to avoid repeated runtime reflective lookups.
private static final Map<String, Class<? extends Annotation>> nameToAnnotation =
Map.ofEntries(new NameToAnnotationEntry("java.lang.Override", Override.class),
new NameToAnnotationEntry("java.lang.annotation.Repeatable", Repeatable.class),
new NameToAnnotationEntry("java.lang.annotation.Target", Target.class),
new NameToAnnotationEntry("BasicAnnoTests.Test", BasicAnnoTests.Test.class),
new NameToAnnotationEntry("BasicAnnoTests.Tests",BasicAnnoTests.Tests.class),
new NameToAnnotationEntry("BasicAnnoTests.TA", BasicAnnoTests.TA.class),
new NameToAnnotationEntry("BasicAnnoTests.TB", BasicAnnoTests.TB.class),
new NameToAnnotationEntry("BasicAnnoTests.TC", BasicAnnoTests.TC.class),
new NameToAnnotationEntry("BasicAnnoTests.TCs", BasicAnnoTests.TCs.class));
static class NameToAnnotationEntry extends AbstractMap.SimpleEntry<String, Class<? extends Annotation>> {
public NameToAnnotationEntry(String key, Class<? extends Annotation> entry) {
super(key, entry);
}
}
@Override
public void init(ProcessingEnvironment pEnv) {
@ -104,7 +124,7 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
}
void error(Element e, String msg) {
messager.printMessage(Kind.ERROR, msg, e);
messager.printError(msg, e);
errors++;
}
@ -263,6 +283,7 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
*/
static AnnotationMirror getAnnotation(AnnotatedConstruct e, String name) {
for (AnnotationMirror m: e.getAnnotationMirrors()) {
checkAnnotatedConstructConsistency(e, m);
TypeElement te = (TypeElement) m.getAnnotationType().asElement();
if (te.getQualifiedName().contentEquals(name)) {
return m;
@ -276,6 +297,7 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
List<AnnotationMirror> res = new ArrayList<>();
for (AnnotationMirror m : e.getAnnotationMirrors()) {
checkAnnotatedConstructConsistency(e, m);
TypeElement te = (TypeElement) m.getAnnotationType().asElement();
if (te.getQualifiedName().contentEquals(name)) {
Compound theAnno = (Compound)m;
@ -290,6 +312,85 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
return res;
}
/**
* Verify that an annotation mirror returned by
* getAnnotationMirrors() has a matching annotation from
* getAnnotation and appropriate values are returned by
* getAnnotationsByType.
*/
static void checkAnnotatedConstructConsistency(AnnotatedConstruct ac, AnnotationMirror m) {
// For each annotation mirror present, an annotation of the
// same annotation type should be directly present as well.
String annotationTypeName = ((TypeElement)m.getAnnotationType().asElement()).getQualifiedName().toString();
var annotationClass = Objects.requireNonNull(nameToAnnotation.get(annotationTypeName));
Annotation a = ac.getAnnotation(annotationClass);
Objects.requireNonNull(a, "Annotation " + m + " not found from getAnnotation(" + annotationTypeName + ")");
// For non-repeating annotation types, getAnnotationsByType should
// wrap a single annotation in an one-element array.
Annotation[] aArray = ac.getAnnotationsByType(annotationClass);
if (aArray.length != 1) {
throw new RuntimeException("Annotation " + m +
" not found from getAnnotationsByType(" + annotationTypeName + ")");
}
// For a container annotation, getAnnotationsByType should
// "look through" and return the contained annotations by
// their annotation class.
var containedAnnotationClass = containerFor(annotationClass);
if (containedAnnotationClass != null) {
Object wrappedAnnotationValue = extractAnnotationValue(a);
int wrappedCount = -1;
if (wrappedAnnotationValue instanceof Annotation[] wrapped) {
wrappedCount = wrapped.length;
}
Annotation[] containedAnnotations = ac.getAnnotationsByType(containedAnnotationClass);
// Check number of contained annotations for consistency
// Given annotation type A and container annotation type As, the result of
// annotatedConstruct.getAnnotaton(As.class).value().length ==
// annotatedConstruct.getAnnotationsByType(A.class).length
// More thorough checking could be done as well, checking
// the types or value of the constituent annotations/AnnotationMirrors.
Object annotationValue = extractAnnotationValue(a);
if (annotationValue instanceof Annotation[] objects
&& objects.length != wrappedCount) {
throw new RuntimeException("\t\t\tmismatched array length : " + objects.length +
"\twrapped count" + wrappedCount);
}
}
}
static Object extractAnnotationValue(Annotation annotation) {
var annotationClass = annotation.annotationType();
try {
// Call value method of the annotation; expected to return
// an array of the contained annotation type.
Method valueMethod = annotationClass.getMethod("value", null);
return valueMethod.invoke(annotation);
} catch (ReflectiveOperationException roe) {
throw new RuntimeException(roe);
}
}
static int wrappedAnnotationCount(Annotation a) {
Object value = extractAnnotationValue(a);
if (value instanceof Annotation[] annotationValueArray) {
return annotationValueArray.length;
} else {
return -1;
}
}
static Class<? extends Annotation> containerFor(Class<? extends Annotation> possibleContainer) {
// Could write this more generally; for now, hard-code particular cases
if (possibleContainer == TCs.class)
return TC.class;
if (possibleContainer == Tests.class)
return Tests.class;
return null;
}
/**
* Get a specific value from an annotation mirror.
*/
@ -402,11 +503,23 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
public @interface TA {
int value();
}
@Target(ElementType.TYPE_USE)
public @interface TB {
int value();
}
@Target(ElementType.TYPE_USE)
@Repeatable(TCs.class)
public @interface TC {
int value();
}
@Target(ElementType.TYPE_USE)
@interface TCs {
TC[] value();
}
// Test cases
// TODO: add more cases for arrays
@ -473,6 +586,21 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
@Test(posn=1, annoType=TA.class, expect="3")
public @TA(3) int @TB(33) [] f3;
@Test(posn=0, annoType=TC.class, expect="1")
public @TC(1) int f1_repeat0;
// Containter annotation @TCs({@TC(2), @TC(3)}) created by the compiler
@Test(posn=0, annoType=TCs.class, expect="{@BasicAnnoTests.TC(2), @BasicAnnoTests.TC(3)}")
public @TC(2) @TC(3) int f1_repeat1;
// Use container explicitly
@Test(posn=0, annoType=TCs.class, expect="{@BasicAnnoTests.TC(4)}")
public @TCs(@TC(4)) int f1_repeat2;
// Explicit empty container
@Test(posn=0, annoType=TCs.class, expect="{}")
public @TCs({}) int f1_repeat3;
@Test(posn=3, annoType=TA.class, expect="4")
public int m1(@TA(4) float a) throws Exception { return 0; }