8198526: getAnnotatedOwnerType does not handle static nested classes correctly
Reviewed-by: jfranck
--- a/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java Thu Dec 20 08:43:23 2018 -0500
+++ b/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java Fri Dec 07 16:56:53 2018 -0800
@@ -100,12 +100,15 @@
if (clz.getEnclosingClass() == null)
return addTo;
if (Modifier.isStatic(clz.getModifiers()))
- return nestingForType(clz.getEnclosingClass(), addTo);
+ return addTo;
return nestingForType(clz.getEnclosingClass(), addTo.pushInner());
} else if (type instanceof ParameterizedType) {
ParameterizedType t = (ParameterizedType)type;
if (t.getOwnerType() == null)
return addTo;
+ if (t.getRawType() instanceof Class
+ && Modifier.isStatic(((Class) t.getRawType()).getModifiers()))
+ return addTo;
return nestingForType(t.getOwnerType(), addTo.pushInner());
}
return addTo;
@@ -193,14 +196,18 @@
if (!(type instanceof Class<?>))
throw new IllegalStateException("Can't compute owner");
- Class<?> inner = (Class<?>)type;
- Class<?> owner = inner.getDeclaringClass();
+ Class<?> nested = (Class<?>)type;
+ Class<?> owner = nested.getDeclaringClass();
if (owner == null) // top-level, local or anonymous
return null;
- if (inner.isPrimitive() || inner == Void.TYPE)
+ if (nested.isPrimitive() || nested == Void.TYPE)
return null;
- LocationInfo outerLoc = nestingForType(owner, getLocation().popAllLocations((byte)1));
+ LocationInfo outerLoc = getLocation().popLocation((byte)1);
+ if (outerLoc == null) {
+ return buildAnnotatedType(owner, LocationInfo.BASE_LOCATION,
+ EMPTY_TYPE_ANNOTATION_ARRAY, EMPTY_TYPE_ANNOTATION_ARRAY, getDecl());
+ }
TypeAnnotation[]all = getTypeAnnotations();
List<TypeAnnotation> l = new ArrayList<>(all.length);
@@ -445,7 +452,12 @@
Type owner = getParameterizedType().getOwnerType();
if (owner == null)
return null;
- LocationInfo outerLoc = nestingForType(owner, getLocation().popAllLocations((byte)1));
+
+ LocationInfo outerLoc = getLocation().popLocation((byte)1);
+ if (outerLoc == null) {
+ return buildAnnotatedType(owner, LocationInfo.BASE_LOCATION,
+ EMPTY_TYPE_ANNOTATION_ARRAY, EMPTY_TYPE_ANNOTATION_ARRAY, getDecl());
+ }
TypeAnnotation[]all = getTypeAnnotations();
List<TypeAnnotation> l = new ArrayList<>(all.length);
--- a/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotation.java Thu Dec 20 08:43:23 2018 -0500
+++ b/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotation.java Fri Dec 07 16:56:53 2018 -0800
@@ -187,19 +187,17 @@
return new LocationInfo(newDepth, res);
}
- /** Pop a series of locations matching {@code tag}. Stop poping as soon as a non-matching tag is found. */
- public LocationInfo popAllLocations(byte tag) {
- LocationInfo l = this;
- int newDepth = l.depth;
- while(newDepth > 0 && l.locations[newDepth - 1].tag == tag) {
- newDepth--;
+ /**
+ * Pops a location matching {@code tag}, or returns {@code null}
+ * if no matching location was found.
+ */
+ public LocationInfo popLocation(byte tag) {
+ if (depth == 0 || locations[depth - 1].tag != tag) {
+ return null;
}
- if (newDepth != l.depth) {
- Location[] res = new Location[newDepth];
- System.arraycopy(this.locations, 0, res, 0, newDepth);
- return new LocationInfo(newDepth, res);
- } else
- return l;
+ Location[] res = new Location[depth - 1];
+ System.arraycopy(locations, 0, res, 0, depth - 1);
+ return new LocationInfo(depth - 1, res);
}
public TypeAnnotation[] filter(TypeAnnotation[] ta) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/annotation/typeAnnotations/GetAnnotatedNestedSuperclass.java Fri Dec 07 16:56:53 2018 -0800
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018, Google LLC. 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 8066967 8198526
+ * @summary Class.getAnnotatedSuperclass() does not correctly extract annotations
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
+import java.util.Arrays;
+import java.util.Objects;
+
+public class GetAnnotatedNestedSuperclass {
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface A {}
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface B {}
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface C {}
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface D {}
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface E {}
+
+ static class X<P1, P2, P3> {}
+
+ static class Y<P1, P2> extends @A X<@B P1, @C P2, @D Class<@E P1>> {}
+
+ public static void main(String[] args) throws Exception {
+ AnnotatedType x = Y.class.getAnnotatedSuperclass();
+ assertEquals(Arrays.toString(x.getAnnotations()), "[@GetAnnotatedNestedSuperclass$A()]");
+ AnnotatedParameterizedType xpt = (AnnotatedParameterizedType) x;
+ {
+ AnnotatedType arg = xpt.getAnnotatedActualTypeArguments()[0];
+ assertEquals(
+ Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass$B()]");
+ }
+ {
+ AnnotatedType arg = xpt.getAnnotatedActualTypeArguments()[1];
+ assertEquals(
+ Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass$C()]");
+ }
+ {
+ AnnotatedType arg = xpt.getAnnotatedActualTypeArguments()[2];
+ assertEquals(
+ Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass$D()]");
+ AnnotatedType nestedArg =
+ ((AnnotatedParameterizedType) arg).getAnnotatedActualTypeArguments()[0];
+ assertEquals(
+ Arrays.toString(nestedArg.getAnnotations()),
+ "[@GetAnnotatedNestedSuperclass$E()]");
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (!Objects.equals(expected, actual)) {
+ throw new AssertionError("expected: " + expected + "; actual=" + actual);
+ }
+ }
+}
--- a/test/jdk/java/lang/annotation/typeAnnotations/GetAnnotatedOwnerType.java Thu Dec 20 08:43:23 2018 -0500
+++ b/test/jdk/java/lang/annotation/typeAnnotations/GetAnnotatedOwnerType.java Fri Dec 07 16:56:53 2018 -0800
@@ -42,6 +42,9 @@
public @TA("non-generic") GetAnnotatedOwnerTypeAuxilliary . @TB("non-generic") Inner nonGeneric;
public @TA("non-generic") GetAnnotatedOwnerTypeAuxilliary . @TB("generic") InnerGeneric<String> innerGeneric;
public @TA("non-generic") GetAnnotatedOwnerTypeAuxilliary . @TB("raw") InnerGeneric innerRaw;
+ public GetAnnotatedOwnerTypeAuxilliary . @TB("non-generic") Nested nestedNonGeneric;
+ public GetAnnotatedOwnerTypeAuxilliary . @TB("generic") NestedGeneric<String> nestedGeneric;
+ public GetAnnotatedOwnerTypeAuxilliary . @TB("raw") NestedGeneric nestedRaw;
public Object anonymous = new Object() {};
public @TA("array") Dummy[] dummy;
public @TA("wildcard") GetAnnotatedOwnerType<?> wildcard;
@@ -58,6 +61,9 @@
testNonGeneric();
testInnerGeneric();
testInnerRaw();
+ testNestedNonGeneric();
+ testNestedGeneric();
+ testNestedRaw();
testLocalClass();
testAnonymousClass();
@@ -155,6 +161,54 @@
+ outer.getAnnotations().length);
}
+ public static void testNestedNonGeneric() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("nestedNonGeneric");
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = f.getAnnotatedType();
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "non-generic");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated, on the correct type
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getType(), ((Class<?>)f.getGenericType()).getEnclosingClass());
+ Asserts.assertTrue(outer.getAnnotations().length == 0, "expecting no annotations, got: "
+ + outer.getAnnotations().length);
+ }
+
+ public static void testNestedGeneric() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("nestedGeneric");
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = f.getAnnotatedType();
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "generic");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated, on the correct type
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getType(), ((ParameterizedType) f.getGenericType()).getOwnerType());
+ Asserts.assertTrue(outer.getAnnotations().length == 0, "expecting no annotations, got: "
+ + outer.getAnnotations().length);
+ }
+
+ public static void testNestedRaw() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("nestedRaw");
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = f.getAnnotatedType();
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "raw");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated, on the correct type
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getType(), ((Class<?>)f.getGenericType()).getEnclosingClass());
+ Asserts.assertTrue(outer.getAnnotations().length == 0, "expecting no annotations, got: "
+ + outer.getAnnotations().length);
+ }
+
public static void testLocalClass() throws Exception {
class ALocalClass {}
class OneMore {
@@ -279,4 +333,8 @@
class Inner {}
class InnerGeneric<Dummy> {}
+
+ static class Nested {}
+
+ static class NestedGeneric<Dummy> {}
}