8198526: getAnnotatedOwnerType does not handle static nested classes correctly
authorcushon
Fri, 07 Dec 2018 16:56:53 -0800
changeset 53080 74d33d22a8df
parent 53079 22295070fcd3
child 53081 3791fee4df3b
8198526: getAnnotatedOwnerType does not handle static nested classes correctly Reviewed-by: jfranck
src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java
src/java.base/share/classes/sun/reflect/annotation/TypeAnnotation.java
test/jdk/java/lang/annotation/typeAnnotations/GetAnnotatedNestedSuperclass.java
test/jdk/java/lang/annotation/typeAnnotations/GetAnnotatedOwnerType.java
--- 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> {}
 }