8256693: getAnnotatedReceiverType parameterizes types too eagerly

Reviewed-by: vromero
This commit is contained in:
Joel Borggrén-Franck 2020-12-18 13:34:36 +00:00
parent 1ce2e94f5f
commit 1cc98bde67
4 changed files with 112 additions and 27 deletions
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++;
}
}