8323684: TypeMirror.{getAnnotationsByType, getAnnotation} return uninformative results
Reviewed-by: jjg
This commit is contained in:
parent
5c874c19cb
commit
a6c0b10704
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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());
|
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.
|
/** Return the base types of a list of types.
|
||||||
*/
|
*/
|
||||||
public static List<Type> baseTypes(List<Type> ts) {
|
public static List<Type> baseTypes(List<Type> ts) {
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8013852 8031744 8225377
|
* @bug 8013852 8031744 8225377 8323684
|
||||||
* @summary Annotations on types
|
* @summary Annotations on types
|
||||||
* @library /tools/javac/lib
|
* @library /tools/javac/lib
|
||||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
* @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.ElementType;
|
||||||
import java.lang.annotation.Repeatable;
|
import java.lang.annotation.Repeatable;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NavigableMap;
|
import java.util.NavigableMap;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -86,6 +89,23 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
|
|||||||
DPrinter dprinter;
|
DPrinter dprinter;
|
||||||
PrintWriter out;
|
PrintWriter out;
|
||||||
boolean verbose = true;
|
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
|
@Override
|
||||||
public void init(ProcessingEnvironment pEnv) {
|
public void init(ProcessingEnvironment pEnv) {
|
||||||
@ -104,7 +124,7 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void error(Element e, String msg) {
|
void error(Element e, String msg) {
|
||||||
messager.printMessage(Kind.ERROR, msg, e);
|
messager.printError(msg, e);
|
||||||
errors++;
|
errors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,6 +283,7 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
|
|||||||
*/
|
*/
|
||||||
static AnnotationMirror getAnnotation(AnnotatedConstruct e, String name) {
|
static AnnotationMirror getAnnotation(AnnotatedConstruct e, String name) {
|
||||||
for (AnnotationMirror m: e.getAnnotationMirrors()) {
|
for (AnnotationMirror m: e.getAnnotationMirrors()) {
|
||||||
|
checkAnnotatedConstructConsistency(e, m);
|
||||||
TypeElement te = (TypeElement) m.getAnnotationType().asElement();
|
TypeElement te = (TypeElement) m.getAnnotationType().asElement();
|
||||||
if (te.getQualifiedName().contentEquals(name)) {
|
if (te.getQualifiedName().contentEquals(name)) {
|
||||||
return m;
|
return m;
|
||||||
@ -276,6 +297,7 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
|
|||||||
List<AnnotationMirror> res = new ArrayList<>();
|
List<AnnotationMirror> res = new ArrayList<>();
|
||||||
|
|
||||||
for (AnnotationMirror m : e.getAnnotationMirrors()) {
|
for (AnnotationMirror m : e.getAnnotationMirrors()) {
|
||||||
|
checkAnnotatedConstructConsistency(e, m);
|
||||||
TypeElement te = (TypeElement) m.getAnnotationType().asElement();
|
TypeElement te = (TypeElement) m.getAnnotationType().asElement();
|
||||||
if (te.getQualifiedName().contentEquals(name)) {
|
if (te.getQualifiedName().contentEquals(name)) {
|
||||||
Compound theAnno = (Compound)m;
|
Compound theAnno = (Compound)m;
|
||||||
@ -290,6 +312,85 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
|
|||||||
return res;
|
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.
|
* Get a specific value from an annotation mirror.
|
||||||
*/
|
*/
|
||||||
@ -402,11 +503,23 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
|
|||||||
public @interface TA {
|
public @interface TA {
|
||||||
int value();
|
int value();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Target(ElementType.TYPE_USE)
|
@Target(ElementType.TYPE_USE)
|
||||||
public @interface TB {
|
public @interface TB {
|
||||||
int value();
|
int value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE_USE)
|
||||||
|
@Repeatable(TCs.class)
|
||||||
|
public @interface TC {
|
||||||
|
int value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE_USE)
|
||||||
|
@interface TCs {
|
||||||
|
TC[] value();
|
||||||
|
}
|
||||||
|
|
||||||
// Test cases
|
// Test cases
|
||||||
|
|
||||||
// TODO: add more cases for arrays
|
// TODO: add more cases for arrays
|
||||||
@ -473,6 +586,21 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
|
|||||||
@Test(posn=1, annoType=TA.class, expect="3")
|
@Test(posn=1, annoType=TA.class, expect="3")
|
||||||
public @TA(3) int @TB(33) [] f3;
|
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")
|
@Test(posn=3, annoType=TA.class, expect="4")
|
||||||
public int m1(@TA(4) float a) throws Exception { return 0; }
|
public int m1(@TA(4) float a) throws Exception { return 0; }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user