8044629: (reflect) Constructor.getAnnotatedReceiverType() returns wrong value
Summary: Update javadoc and align implementation with new spec
Reviewed-by: psandoz
--- a/jdk/src/share/classes/java/lang/reflect/Constructor.java Tue Aug 12 14:57:36 2014 +0100
+++ b/jdk/src/share/classes/java/lang/reflect/Constructor.java Tue Aug 12 10:16:58 2014 +0200
@@ -544,15 +544,33 @@
*/
@Override
public AnnotatedType getAnnotatedReceiverType() {
- if (getDeclaringClass().getEnclosingClass() == null)
- return super.getAnnotatedReceiverType();
+ Class<?> thisDeclClass = getDeclaringClass();
+ Class<?> enclosingClass = thisDeclClass.getEnclosingClass();
+
+ if (enclosingClass == null) {
+ // A Constructor for a top-level class
+ return null;
+ }
+ Class<?> outerDeclaringClass = thisDeclClass.getDeclaringClass();
+ if (outerDeclaringClass == null) {
+ // A constructor for a local or anonymous class
+ return null;
+ }
+
+ // Either static nested or inner class
+ if (Modifier.isStatic(thisDeclClass.getModifiers())) {
+ // static nested
+ return null;
+ }
+
+ // A Constructor for an inner class
return TypeAnnotationParser.buildAnnotatedType(getTypeAnnotationBytes0(),
sun.misc.SharedSecrets.getJavaLangAccess().
- getConstantPool(getDeclaringClass()),
+ getConstantPool(thisDeclClass),
this,
- getDeclaringClass(),
- getDeclaringClass().getEnclosingClass(),
+ thisDeclClass,
+ enclosingClass,
TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER);
}
}
--- a/jdk/src/share/classes/java/lang/reflect/Executable.java Tue Aug 12 14:57:36 2014 +0100
+++ b/jdk/src/share/classes/java/lang/reflect/Executable.java Tue Aug 12 10:16:58 2014 +0200
@@ -585,21 +585,24 @@
/**
* Returns an {@code AnnotatedType} object that represents the use of a
* type to specify the receiver type of the method/constructor represented
- * by this Executable object. The receiver type of a method/constructor is
- * available only if the method/constructor has a <em>receiver
- * parameter</em> (JLS 8.4.1).
+ * by this {@code Executable} object.
*
- * If this {@code Executable} object represents a constructor or instance
- * method that does not have a receiver parameter, or has a receiver
- * parameter with no annotations on its type, then the return value is an
- * {@code AnnotatedType} object representing an element with no
+ * The receiver type of a method/constructor is available only if the
+ * method/constructor has a receiver parameter (JLS 8.4.1). If this {@code
+ * Executable} object <em>represents an instance method or represents a
+ * constructor of an inner member class</em>, and the
+ * method/constructor <em>either</em> has no receiver parameter or has a
+ * receiver parameter with no annotations on its type, then the return
+ * value is an {@code AnnotatedType} object representing an element with no
* annotations.
*
- * If this {@code Executable} object represents a static method, then the
- * return value is null.
+ * If this {@code Executable} object represents a static method or
+ * represents a constructor of a top level, static member, local, or
+ * anoymous class, then the return value is null.
*
* @return an object representing the receiver type of the method or
- * constructor represented by this {@code Executable}
+ * constructor represented by this {@code Executable} or {@code null} if
+ * this {@code Executable} can not have a receiver parameter
*/
public AnnotatedType getAnnotatedReceiverType() {
if (Modifier.isStatic(this.getModifiers()))
--- a/jdk/test/java/lang/annotation/typeAnnotations/ConstructorReceiverTest.java Tue Aug 12 14:57:36 2014 +0100
+++ b/jdk/test/java/lang/annotation/typeAnnotations/ConstructorReceiverTest.java Tue Aug 12 10:16:58 2014 +0200
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8023651
+ * @bug 8023651 8044629
* @summary Test that the receiver annotations and the return annotations of
* constructors behave correctly.
* @run testng ConstructorReceiverTest
@@ -38,11 +38,16 @@
import static org.testng.Assert.*;
public class ConstructorReceiverTest {
+ public static final Integer EMPTY_ANNOTATED_TYPE = Integer.valueOf(-1);
+
// Format is {
// { Class to get ctor for,
// ctor param class,
// value of anno of return type,
- // value of anno for receiver or null if there should be no receiver anno
+ // value of anno for receiver,
+ // or null if there should be no receiver,
+ // or EMPTY_ANNOTATED_TYPE of there should be a receiver but
+ // no annotation
// },
// ...
// }
@@ -51,13 +56,15 @@
{ ConstructorReceiverTest.Middle.class, ConstructorReceiverTest.class, Integer.valueOf(10), Integer.valueOf(15) },
{ ConstructorReceiverTest.Middle.Inner.class, ConstructorReceiverTest.Middle.class, Integer.valueOf(100), Integer.valueOf(150) },
{ ConstructorReceiverTest.Middle.Inner.Innermost.class, ConstructorReceiverTest.Middle.Inner.class, Integer.valueOf(1000), Integer.valueOf(1500) },
- { ConstructorReceiverTest.Middle.InnerNoReceiver.class, ConstructorReceiverTest.Middle.class, Integer.valueOf(300), null },
+ { ConstructorReceiverTest.Middle.InnerNoReceiver.class, ConstructorReceiverTest.Middle.class, Integer.valueOf(300), EMPTY_ANNOTATED_TYPE },
{ ConstructorReceiverTest.Nested.class, null, Integer.valueOf(20), null },
{ ConstructorReceiverTest.Nested.NestedMiddle.class, ConstructorReceiverTest.Nested.class, Integer.valueOf(200), Integer.valueOf(250)},
{ ConstructorReceiverTest.Nested.NestedMiddle.NestedInner.class, ConstructorReceiverTest.Nested.NestedMiddle.class, Integer.valueOf(2000), Integer.valueOf(2500)},
- { ConstructorReceiverTest.Nested.NestedMiddle.NestedInnerNoReceiver.class, ConstructorReceiverTest.Nested.NestedMiddle.class, Integer.valueOf(4000), null},
+ { ConstructorReceiverTest.Nested.NestedMiddle.NestedInnerNoReceiver.class, ConstructorReceiverTest.Nested.NestedMiddle.class, Integer.valueOf(4000), EMPTY_ANNOTATED_TYPE},
+ { ConstructorReceiverTest.Nested.NestedMiddle.SecondNestedInnerNoReceiver.class, ConstructorReceiverTest.Nested.NestedMiddle.class, Integer.valueOf(5000), EMPTY_ANNOTATED_TYPE},
};
+
@DataProvider
public Object[][] data() { return TESTS; }
@@ -71,14 +78,27 @@
c = toTest.getDeclaredConstructor(ctorParamType);
AnnotatedType annotatedReceiverType = c.getAnnotatedReceiverType();
- Annotation[] receiverAnnotations = annotatedReceiverType.getAnnotations();
+ // Some Constructors doesn't conceptually have a receiver, they should return null
if (receiverVal == null) {
- assertEquals(receiverAnnotations.length, 0, Arrays.asList(receiverAnnotations).toString() +
- " should be empty. Looking at 'length': ");
+ assertNull(annotatedReceiverType, "getAnnotatedReciverType should return null for Constructor: " + c);
return;
}
+ // check that getType() matches the receiver
+ assertEquals(annotatedReceiverType.getType(),
+ ctorParamType,
+ "getType() doesn't match receiver type: " + ctorParamType);
+
+ Annotation[] receiverAnnotations = annotatedReceiverType.getAnnotations();
+
+ // Some Constructors have no annotations on but in theory can have a receiver
+ if (receiverVal.equals(EMPTY_ANNOTATED_TYPE)) {
+ assertEquals(receiverAnnotations.length, 0, "expecting an empty annotated type for: " + c);
+ return;
+ }
+
+ // The rest should have annotations
assertEquals(receiverAnnotations.length, 1, "expecting a 1 element array. Looking at 'length': ");
assertEquals(((Annot)receiverAnnotations[0]).value(), receiverVal.intValue(), " wrong annotation found. Found " +
receiverAnnotations[0] +
@@ -136,6 +156,10 @@
class NestedInnerNoReceiver {
@Annot(4000) public NestedInnerNoReceiver() {}
}
+
+ class SecondNestedInnerNoReceiver {
+ @Annot(5000) public SecondNestedInnerNoReceiver(NestedMiddle NestedMiddle.this) {}
+ }
}
}
--- a/jdk/test/java/lang/annotation/typeAnnotations/GetAnnotatedReceiverType.java Tue Aug 12 14:57:36 2014 +0100
+++ b/jdk/test/java/lang/annotation/typeAnnotations/GetAnnotatedReceiverType.java Tue Aug 12 10:16:58 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014 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
@@ -23,10 +23,11 @@
/*
* @test
- * @bug 8024915
+ * @bug 8024915 8044629
*/
import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Executable;
import java.util.Arrays;
public class GetAnnotatedReceiverType {
@@ -42,41 +43,115 @@
public Inner1(GetAnnotatedReceiverType GetAnnotatedReceiverType.this) {}
}
+ public static class Nested {
+ public Nested() {}
+
+ public class NestedInner {
+ public NestedInner() { }
+
+ public Class<?> getLocalClass () {
+ class NestedInnerLocal { public NestedInnerLocal() {} }
+ return NestedInnerLocal.class;
+ }
+
+ public Class<?> getAnonymousClass() {
+ return new Object() {}.getClass();
+ }
+ }
+ }
+
+ public class Inner2 {
+ public Inner2() { }
+
+ public class Inner3 {
+ public Inner3() { }
+
+ public Class<?> getLocalClass () {
+ class InnerLocal { public InnerLocal() {} }
+ return InnerLocal.class;
+ }
+
+ public Class<?> getAnonymousClass() {
+ return new Object() {}.getClass();
+ }
+ }
+
+ public Class<?> getLocalClass () {
+ class InnerLocal { public InnerLocal() {} }
+ return InnerLocal.class;
+ }
+
+ public Class<?> getAnonymousClass() {
+ return new Object() {}.getClass();
+ }
+ }
+
private static int failures = 0;
private static int tests = 0;
public static void main(String[] args) throws NoSuchMethodException {
- checkEmptyAT(GetAnnotatedReceiverType.class.getMethod("method").getAnnotatedReceiverType(),
+ checkEmptyAT(GetAnnotatedReceiverType.class.getMethod("method"),
"getAnnotatedReceiverType for \"method\" should return an empty AnnotatedType");
- checkEmptyAT(Inner0.class.getConstructor(GetAnnotatedReceiverType.class).getAnnotatedReceiverType(),
+ checkEmptyAT(Inner0.class.getConstructor(GetAnnotatedReceiverType.class),
"getAnnotatedReceiverType for a ctor without a \"this\" should return an empty AnnotatedType");
- checkEmptyAT(GetAnnotatedReceiverType.class.getMethod("method0").getAnnotatedReceiverType(),
+ checkEmptyAT(GetAnnotatedReceiverType.class.getMethod("method0"),
"getAnnotatedReceiverType for \"method0\" should return an empty AnnotatedType");
- checkEmptyAT(Inner1.class.getConstructor(GetAnnotatedReceiverType.class).getAnnotatedReceiverType(),
+ checkEmptyAT(Inner1.class.getConstructor(GetAnnotatedReceiverType.class),
"getAnnotatedReceiverType for a ctor with a \"this\" should return an empty AnnotatedType");
- checkNull(GetAnnotatedReceiverType.class.getMethod("method4").getAnnotatedReceiverType(),
+ checkNull(GetAnnotatedReceiverType.class.getMethod("method4"),
"getAnnotatedReceiverType() on a static method should return null");
+ // More nested, inner, local and anonymous classes
+ Nested nested = new Nested();
+ Nested.NestedInner instance = nested.new NestedInner();
+ checkNull(nested.getClass().getConstructors()[0],
+ "getAnnotatedReceiverType() on a constructor for a static class should return null");
+ checkEmptyAT(instance.getClass().getConstructors()[0],
+ "getAnnotatedReceiverType for a ctor without a \"this\" should return an empty AnnotatedType");
+ checkNull(instance.getLocalClass().getConstructors()[0],
+ "getAnnotatedReceiverType() on a constructor for a local class should return null");
+ checkNull(instance.getAnonymousClass().getDeclaredConstructors()[0],
+ "getAnnotatedReceiverType() on a constructor for an anonymous class should return null");
+
+ GetAnnotatedReceiverType outer = new GetAnnotatedReceiverType();
+ Inner2 instance2 = outer.new Inner2();
+ checkEmptyAT(instance2.getClass().getConstructors()[0],
+ "getAnnotatedReceiverType for a ctor without a \"this\" should return an empty AnnotatedType");
+ checkNull(instance2.getLocalClass().getConstructors()[0],
+ "getAnnotatedReceiverType() on a constructor for a local class should return null");
+ checkNull(instance2.getAnonymousClass().getDeclaredConstructors()[0],
+ "getAnnotatedReceiverType() on a constructor for an anonymous class should return null");
+
+ Inner2.Inner3 instance3 = instance2.new Inner3();
+ checkEmptyAT(instance3.getClass().getConstructors()[0],
+ "getAnnotatedReceiverType for a ctor without a \"this\" should return an empty AnnotatedType");
+ checkNull(instance3.getLocalClass().getConstructors()[0],
+ "getAnnotatedReceiverType() on a constructor for a local class should return null");
+ checkNull(instance3.getAnonymousClass().getDeclaredConstructors()[0],
+ "getAnnotatedReceiverType() on a constructor for an anonymous class should return null");
+
if (failures != 0)
throw new RuntimeException("Test failed, see log for details");
- else if (tests != 5)
+ else if (tests != 15)
throw new RuntimeException("Not all cases ran, failing");
}
- private static void checkNull(Object o, String msg) {
- if (o != null) {
+ private static void checkNull(Executable e, String msg) {
+ AnnotatedType a = e.getAnnotatedReceiverType();
+ if (a != null) {
failures++;
- System.err.println(msg);
+ System.err.println(msg + ": " + e);
}
tests++;
}
- private static void checkEmptyAT(AnnotatedType a, String msg) {
+ private static void checkEmptyAT(Executable e, String msg) {
+ AnnotatedType a = e.getAnnotatedReceiverType();
if (a.getAnnotations().length != 0) {
failures++;
- System.err.print(msg);
+ System.err.print(msg + ": " + e);
}
tests++;
}
--- a/jdk/test/java/lang/annotation/typeAnnotations/TestExecutableGetAnnotatedType.java Tue Aug 12 14:57:36 2014 +0100
+++ b/jdk/test/java/lang/annotation/typeAnnotations/TestExecutableGetAnnotatedType.java Tue Aug 12 10:16:58 2014 +0200
@@ -73,13 +73,11 @@
testParameters(e.getParameters());
}
- // should test constructors as well, see JDK-8044629
@Test(dataProvider = "genericMethodData")
public void testGenericReceiverType(Executable e) throws Exception {
testReceiverType0(e);
}
- // should test constructors as well, see JDK-8044629
@Test(dataProvider = "methodData")
public void testReceiverType(Executable e) throws Exception {
testReceiverType0(e);