8256693: getAnnotatedReceiverType parameterizes types too eagerly
Reviewed-by: vromero
This commit is contained in:
parent
1ce2e94f5f
commit
1cc98bde67
@ -38,6 +38,10 @@ public interface AnnotatedParameterizedType extends AnnotatedType {
|
|||||||
/**
|
/**
|
||||||
* Returns the potentially annotated actual type arguments of this parameterized type.
|
* 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
|
* @return the potentially annotated actual type arguments of this parameterized type
|
||||||
* @see ParameterizedType#getActualTypeArguments()
|
* @see ParameterizedType#getActualTypeArguments()
|
||||||
*/
|
*/
|
||||||
|
@ -662,7 +662,7 @@ public final class Constructor<T> extends Executable {
|
|||||||
getConstantPool(thisDeclClass),
|
getConstantPool(thisDeclClass),
|
||||||
this,
|
this,
|
||||||
thisDeclClass,
|
thisDeclClass,
|
||||||
resolveToOwnerType(enclosingClass),
|
parameterize(enclosingClass),
|
||||||
TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER);
|
TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -699,10 +699,29 @@ public abstract class Executable extends AccessibleObject
|
|||||||
getConstantPool(getDeclaringClass()),
|
getConstantPool(getDeclaringClass()),
|
||||||
this,
|
this,
|
||||||
getDeclaringClass(),
|
getDeclaringClass(),
|
||||||
resolveToOwnerType(getDeclaringClass()),
|
parameterize(getDeclaringClass()),
|
||||||
TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER);
|
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
|
* Returns an array of {@code AnnotatedType} objects that represent the use
|
||||||
* of types to specify formal parameter types of the method/constructor
|
* of types to specify formal parameter types of the method/constructor
|
||||||
@ -753,24 +772,4 @@ public abstract class Executable extends AccessibleObject
|
|||||||
getGenericExceptionTypes(),
|
getGenericExceptionTypes(),
|
||||||
TypeAnnotation.TypeAnnotationTarget.THROWS);
|
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
|
* @test
|
||||||
* @bug 8024915 8044629
|
* @bug 8024915 8044629 8256693
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.lang.reflect.AnnotatedType;
|
import java.lang.reflect.*;
|
||||||
import java.lang.reflect.Executable;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class GetAnnotatedReceiverType {
|
public class GetAnnotatedReceiverType {
|
||||||
public void method() {}
|
public void method() {}
|
||||||
@ -62,9 +60,15 @@ public class GetAnnotatedReceiverType {
|
|||||||
|
|
||||||
public class Inner2 {
|
public class Inner2 {
|
||||||
public Inner2() { }
|
public Inner2() { }
|
||||||
|
public void innerMethod2(GetAnnotatedReceiverType.Inner2 this) {}
|
||||||
|
|
||||||
public class Inner3 {
|
public class Inner3 {
|
||||||
public 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 () {
|
public Class<?> getLocalClass () {
|
||||||
class InnerLocal { public InnerLocal() {} }
|
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 failures = 0;
|
||||||
private static int tests = 0;
|
private static int tests = 0;
|
||||||
|
private static final int EXPECTED_TEST_CASES = 25;
|
||||||
|
|
||||||
public static void main(String[] args) throws NoSuchMethodException {
|
public static void main(String[] args) throws NoSuchMethodException {
|
||||||
checkEmptyAT(GetAnnotatedReceiverType.class.getMethod("method"),
|
checkEmptyAT(GetAnnotatedReceiverType.class.getMethod("method"),
|
||||||
@ -132,9 +151,35 @@ public class GetAnnotatedReceiverType {
|
|||||||
checkNull(instance3.getAnonymousClass().getDeclaredConstructors()[0],
|
checkNull(instance3.getAnonymousClass().getDeclaredConstructors()[0],
|
||||||
"getAnnotatedReceiverType() on a constructor for an anonymous class should return null");
|
"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)
|
if (failures != 0)
|
||||||
throw new RuntimeException("Test failed, see log for details");
|
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");
|
throw new RuntimeException("Not all cases ran, failing");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,4 +200,41 @@ public class GetAnnotatedReceiverType {
|
|||||||
}
|
}
|
||||||
tests++;
|
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