8256693: getAnnotatedReceiverType parameterizes types too eagerly
Reviewed-by: vromero
This commit is contained in:
parent
1ce2e94f5f
commit
1cc98bde67
src/java.base/share/classes/java/lang/reflect
test/jdk/java/lang/annotation/typeAnnotations
@ -38,6 +38,10 @@ public interface AnnotatedParameterizedType extends AnnotatedType {
|
||||
/**
|
||||
* Returns the potentially annotated actual type arguments of this parameterized type.
|
||||
*
|
||||
* <p>Note that in some cases, the returned array can be empty. This can occur
|
||||
* if this annotated type represents a non-parameterized type nested within
|
||||
* a parameterized type.
|
||||
*
|
||||
* @return the potentially annotated actual type arguments of this parameterized type
|
||||
* @see ParameterizedType#getActualTypeArguments()
|
||||
*/
|
||||
|
@ -662,7 +662,7 @@ public final class Constructor<T> extends Executable {
|
||||
getConstantPool(thisDeclClass),
|
||||
this,
|
||||
thisDeclClass,
|
||||
resolveToOwnerType(enclosingClass),
|
||||
parameterize(enclosingClass),
|
||||
TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER);
|
||||
}
|
||||
}
|
||||
|
@ -699,10 +699,29 @@ public abstract class Executable extends AccessibleObject
|
||||
getConstantPool(getDeclaringClass()),
|
||||
this,
|
||||
getDeclaringClass(),
|
||||
resolveToOwnerType(getDeclaringClass()),
|
||||
parameterize(getDeclaringClass()),
|
||||
TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER);
|
||||
}
|
||||
|
||||
Type parameterize(Class<?> c) {
|
||||
Class<?> ownerClass = c.getDeclaringClass();
|
||||
TypeVariable<?>[] typeVars = c.getTypeParameters();
|
||||
|
||||
if (ownerClass == null) { // base case
|
||||
if (typeVars.length == 0)
|
||||
return c;
|
||||
else
|
||||
return ParameterizedTypeImpl.make(c, typeVars, null);
|
||||
}
|
||||
|
||||
// Resolve owner
|
||||
Type ownerType = parameterize(ownerClass);
|
||||
if (ownerType instanceof Class<?> && typeVars.length == 0) // We have yet to encounter type parameters
|
||||
return c;
|
||||
else
|
||||
return ParameterizedTypeImpl.make(c, typeVars, ownerType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of {@code AnnotatedType} objects that represent the use
|
||||
* of types to specify formal parameter types of the method/constructor
|
||||
@ -753,24 +772,4 @@ public abstract class Executable extends AccessibleObject
|
||||
getGenericExceptionTypes(),
|
||||
TypeAnnotation.TypeAnnotationTarget.THROWS);
|
||||
}
|
||||
|
||||
static Type resolveToOwnerType(Class<?> c) {
|
||||
TypeVariable<?>[] v = c.getTypeParameters();
|
||||
Type o = resolveOwner(c);
|
||||
Type t;
|
||||
if (o != null || v.length > 0) {
|
||||
t = ParameterizedTypeImpl.make(c, v, o);
|
||||
} else {
|
||||
t = c;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
private static Type resolveOwner(Class<?> t) {
|
||||
if (Modifier.isStatic(t.getModifiers()) || !(t.isLocalClass() || t.isMemberClass() || t.isAnonymousClass())) {
|
||||
return null;
|
||||
}
|
||||
Class<?> d = t.getDeclaringClass();
|
||||
return ParameterizedTypeImpl.make(d, d.getTypeParameters(), resolveOwner(d));
|
||||
}
|
||||
}
|
||||
|
@ -23,12 +23,10 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8024915 8044629
|
||||
* @bug 8024915 8044629 8256693
|
||||
*/
|
||||
|
||||
import java.lang.reflect.AnnotatedType;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.util.Arrays;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
public class GetAnnotatedReceiverType {
|
||||
public void method() {}
|
||||
@ -62,9 +60,15 @@ public class GetAnnotatedReceiverType {
|
||||
|
||||
public class Inner2 {
|
||||
public Inner2() { }
|
||||
public void innerMethod2(GetAnnotatedReceiverType.Inner2 this) {}
|
||||
|
||||
public class Inner3 {
|
||||
public Inner3() { }
|
||||
public void innerMethod3(GetAnnotatedReceiverType.Inner2.Inner3 this) {}
|
||||
|
||||
public class Inner7<T> {
|
||||
public void innerMethod7(GetAnnotatedReceiverType.Inner2.Inner3.Inner7<T> this) {}
|
||||
}
|
||||
|
||||
public Class<?> getLocalClass () {
|
||||
class InnerLocal { public InnerLocal() {} }
|
||||
@ -86,8 +90,23 @@ public class GetAnnotatedReceiverType {
|
||||
}
|
||||
}
|
||||
|
||||
public class Inner4<T> {
|
||||
public Inner4(GetAnnotatedReceiverType GetAnnotatedReceiverType.this) {}
|
||||
public void innerMethod4(GetAnnotatedReceiverType.Inner4<T> this) {}
|
||||
|
||||
public class Inner5 {
|
||||
public Inner5(GetAnnotatedReceiverType.Inner4<T> GetAnnotatedReceiverType.Inner4.this) {}
|
||||
public void innerMethod5(GetAnnotatedReceiverType.Inner4<T>.Inner5 this) {}
|
||||
|
||||
public class Inner6 {
|
||||
public Inner6(GetAnnotatedReceiverType.Inner4<T>.Inner5 GetAnnotatedReceiverType.Inner4.Inner5.this) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int failures = 0;
|
||||
private static int tests = 0;
|
||||
private static final int EXPECTED_TEST_CASES = 25;
|
||||
|
||||
public static void main(String[] args) throws NoSuchMethodException {
|
||||
checkEmptyAT(GetAnnotatedReceiverType.class.getMethod("method"),
|
||||
@ -132,9 +151,35 @@ public class GetAnnotatedReceiverType {
|
||||
checkNull(instance3.getAnonymousClass().getDeclaredConstructors()[0],
|
||||
"getAnnotatedReceiverType() on a constructor for an anonymous class should return null");
|
||||
|
||||
Inner4<?> instance4 = outer.new Inner4<String>();
|
||||
Inner4<?>.Inner5 instance5 = instance4.new Inner5();
|
||||
Inner4<?>.Inner5.Inner6 instance6 = instance5.new Inner6();
|
||||
|
||||
checkAnnotatedReceiverType(instance4.getClass().getConstructors()[0], false,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this constructor should be");
|
||||
checkAnnotatedReceiverType(instance5.getClass().getConstructors()[0], true,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this constructor should be");
|
||||
checkAnnotatedReceiverType(instance6.getClass().getConstructors()[0], true,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this constructor should be");
|
||||
checkAnnotatedReceiverType(outer.getClass().getMethod("method0"), false,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this method should be");
|
||||
checkAnnotatedReceiverType(instance4.getClass().getMethod("innerMethod4"), true,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this method should be");
|
||||
checkAnnotatedReceiverType(instance5.getClass().getMethod("innerMethod5"), true,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this method should be");
|
||||
checkAnnotatedReceiverType(instance2.getClass().getMethod("innerMethod2"), false,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this method should be");
|
||||
checkAnnotatedReceiverType(instance3.getClass().getMethod("innerMethod3"), false,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this method should be");
|
||||
|
||||
Inner2.Inner3.Inner7<?> instance7 = instance3.new Inner7<String>();
|
||||
checkAnnotatedReceiverType(instance7.getClass().getMethod("innerMethod7"), true,
|
||||
"The type of .getAnnotatedReceiverType().getType() for this method should be");
|
||||
recursiveCheckAnnotatedOwnerTypes(instance7.getClass().getMethod("innerMethod7").getAnnotatedReceiverType());
|
||||
|
||||
if (failures != 0)
|
||||
throw new RuntimeException("Test failed, see log for details");
|
||||
else if (tests != 15)
|
||||
else if (tests != EXPECTED_TEST_CASES)
|
||||
throw new RuntimeException("Not all cases ran, failing");
|
||||
}
|
||||
|
||||
@ -155,4 +200,41 @@ public class GetAnnotatedReceiverType {
|
||||
}
|
||||
tests++;
|
||||
}
|
||||
|
||||
private static void checkAnnotatedReceiverType(Executable e, boolean shouldBeParameterized, String msg) {
|
||||
Type t = e.getAnnotatedReceiverType().getType();
|
||||
if (shouldBeParameterized != (t instanceof ParameterizedType)) {
|
||||
failures++;
|
||||
System.err.println(e + ", " + msg + " " + (shouldBeParameterized ? "ParameterizedType" : "Class") + ", found: " + t.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
// Test we can get the potentially empty annotated actual type arguments array
|
||||
if (shouldBeParameterized) {
|
||||
try {
|
||||
ParameterizedType t1 = (ParameterizedType)t;
|
||||
AnnotatedParameterizedType at1 = (AnnotatedParameterizedType)e.getAnnotatedReceiverType();
|
||||
|
||||
if (t1.getActualTypeArguments().length != at1.getAnnotatedActualTypeArguments().length) {
|
||||
System.err.println(t1 + "'s actual type arguments can't match " + at1);
|
||||
failures++;
|
||||
}
|
||||
} catch (ClassCastException cce) {
|
||||
System.err.println("Couldn't get potentially empty actual type arguments: " + cce.getMessage());
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
tests++;
|
||||
}
|
||||
|
||||
private static void recursiveCheckAnnotatedOwnerTypes(AnnotatedType t) {
|
||||
AnnotatedType check = t.getAnnotatedOwnerType();
|
||||
do {
|
||||
if (!(check.getType() instanceof Class<?>)) {
|
||||
failures++;
|
||||
System.err.println("Expecting only instances of Class returned for .getType() found " + check.getType().getClass().getSimpleName());
|
||||
}
|
||||
check = check.getAnnotatedOwnerType();
|
||||
} while (check != null);
|
||||
tests++;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user