8202471: (ann) Cannot read type annotations on generic receiver type's type variables

Reviewed-by: jfranck
This commit is contained in:
Rafael Winterhalter 2020-11-17 11:23:47 +00:00 committed by Joel Borggrén-Franck
parent adb8561aba
commit 53a31889fe
7 changed files with 242 additions and 7 deletions

View File

@ -662,7 +662,7 @@ public final class Constructor<T> extends Executable {
getConstantPool(thisDeclClass),
this,
thisDeclClass,
enclosingClass,
resolveToOwnerType(enclosingClass),
TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER);
}
}

View File

@ -38,6 +38,7 @@ import sun.reflect.annotation.AnnotationParser;
import sun.reflect.annotation.AnnotationSupport;
import sun.reflect.annotation.TypeAnnotationParser;
import sun.reflect.annotation.TypeAnnotation;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import sun.reflect.generics.repository.ConstructorRepository;
/**
@ -698,7 +699,7 @@ public abstract class Executable extends AccessibleObject
getConstantPool(getDeclaringClass()),
this,
getDeclaringClass(),
getDeclaringClass(),
resolveToOwnerType(getDeclaringClass()),
TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER);
}
@ -753,4 +754,23 @@ public abstract class Executable extends AccessibleObject
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));
}
}

View File

@ -85,10 +85,16 @@ public class ConstructorReceiverTest {
return;
}
// check that getType() matches the receiver
assertEquals(annotatedReceiverType.getType(),
ctorParamType,
"getType() doesn't match receiver type: " + ctorParamType);
// check that getType() matches the receiver (which can be parameterized)
if (annotatedReceiverType.getType() instanceof ParameterizedType) {
assertEquals(((ParameterizedType) annotatedReceiverType.getType()).getRawType(),
ctorParamType,
"getType() doesn't match receiver type: " + ctorParamType);
} else {
assertEquals(annotatedReceiverType.getType(),
ctorParamType,
"getType() doesn't match receiver type: " + ctorParamType);
}
Annotation[] receiverAnnotations = annotatedReceiverType.getAnnotations();

View File

@ -75,7 +75,7 @@ public class TestExecutableGetAnnotatedType {
@Test(dataProvider = "genericMethodData")
public void testGenericReceiverType(Executable e) throws Exception {
testReceiverType0(e);
testParameterizedReceiverType0(e);
}
@Test(dataProvider = "methodData")
@ -136,6 +136,15 @@ public class TestExecutableGetAnnotatedType {
assertSame(e.getAnnotatedReceiverType().getType(), e.getDeclaringClass());
}
private void testParameterizedReceiverType0(Executable e) {
if (Modifier.isStatic(e.getModifiers()))
assertNull(e.getAnnotatedReceiverType());
else {
assertTrue(e.getAnnotatedReceiverType().getType() instanceof ParameterizedType);
assertSame(((ParameterizedType) e.getAnnotatedReceiverType().getType()).getRawType(), e.getDeclaringClass());
}
}
private void testReturnType(Method m) {
Type t = m.getGenericReturnType();
AnnotatedType at = m.getAnnotatedReturnType();

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2020, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8202471
* @summary A nested class's owner can be type annotated if used as a receiver type
*/
import java.lang.annotation.Target;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
public class TestReceiverTypeOwner<T> {
public static void main(String[] args) throws NoSuchMethodException {
Method method = TestReceiverTypeOwner.Inner.class.getDeclaredMethod("m");
AnnotatedType receiverType = method.getAnnotatedReceiverType();
AnnotatedParameterizedType parameterizedType = (AnnotatedParameterizedType) receiverType;
AnnotatedType owner = parameterizedType.getAnnotatedOwnerType();
Annotation[] annotations = owner.getAnnotations();
if (annotations.length != 1 || !(annotations[0] instanceof TypeAnnotation)) {
throw new AssertionError();
}
}
class Inner {
void m(@TypeAnnotation TestReceiverTypeOwner<T>.Inner this) { }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface TypeAnnotation { }
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2020, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8202471
* @summary A constructor's parameterized receiver type's type variables can be type annotated
*/
import java.lang.annotation.Target;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
public class TestReceiverTypeParameterizedConstructor<T> {
public static void main(String[] args) throws NoSuchMethodException {
doAssert(TestReceiverTypeParameterizedConstructor.Inner.class);
doAssert(TestReceiverTypeParameterizedConstructor.Inner.Inner2.class);
}
private static void doAssert(Class<?> c) throws NoSuchMethodException {
Constructor<?> constructor = c.getDeclaredConstructor(c.getDeclaringClass());
AnnotatedType receiverType = constructor.getAnnotatedReceiverType();
AnnotatedParameterizedType parameterizedType = (AnnotatedParameterizedType) receiverType;
int count = 0;
do {
AnnotatedType[] arguments = parameterizedType.getAnnotatedActualTypeArguments();
Annotation[] annotations = arguments[0].getAnnotations();
if (annotations.length != 1
|| !(annotations[0] instanceof TypeAnnotation)
|| ((TypeAnnotation) annotations[0]).value() != count++) {
throw new AssertionError();
}
parameterizedType = (AnnotatedParameterizedType) parameterizedType.getAnnotatedOwnerType();
} while (parameterizedType != null);
}
class Inner<S> {
Inner(TestReceiverTypeParameterizedConstructor<@TypeAnnotation(0) T> TestReceiverTypeParameterizedConstructor.this) { }
class Inner2 {
Inner2(TestReceiverTypeParameterizedConstructor<@TypeAnnotation(1) T>.Inner<@TypeAnnotation(0) S> TestReceiverTypeParameterizedConstructor.Inner.this) { }
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface TypeAnnotation {
int value();
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2020, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8202471
* @summary A method's parameterized receiver type's type variables can be type annotated
*/
import java.lang.annotation.Target;
import java.lang.annotation.*;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
public class TestReceiverTypeParameterizedMethod<T> {
public static void main(String[] args) throws NoSuchMethodException {
doAssert(TestReceiverTypeParameterizedMethod.class);
doAssert(TestReceiverTypeParameterizedMethod.Inner.class);
}
private static void doAssert(Class<?> c) throws NoSuchMethodException {
Method method = c.getDeclaredMethod("m");
AnnotatedType receiverType = method.getAnnotatedReceiverType();
AnnotatedParameterizedType parameterizedType = (AnnotatedParameterizedType) receiverType;
int count = 0;
do {
AnnotatedType[] arguments = parameterizedType.getAnnotatedActualTypeArguments();
Annotation[] annotations = arguments[0].getAnnotations();
if (annotations.length != 1
|| !(annotations[0] instanceof TypeAnnotation)
|| ((TypeAnnotation) annotations[0]).value() != count++) {
throw new AssertionError();
}
parameterizedType = (AnnotatedParameterizedType) parameterizedType.getAnnotatedOwnerType();
} while (parameterizedType != null);
}
void m(TestReceiverTypeParameterizedMethod<@TypeAnnotation(0) T> this) { }
class Inner<S> {
void m(TestReceiverTypeParameterizedMethod<@TypeAnnotation(1) T>.Inner<@TypeAnnotation(0) S> this) { }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface TypeAnnotation {
int value();
}
}