8198526: getAnnotatedOwnerType does not handle static nested classes correctly

Reviewed-by: jfranck
This commit is contained in:
Liam Miller-Cushon 2018-12-07 16:56:53 -08:00
parent 265abce7fb
commit 1cd847d6d1
4 changed files with 182 additions and 18 deletions

View file

@ -100,12 +100,15 @@ public final class AnnotatedTypeFactory {
if (clz.getEnclosingClass() == null) if (clz.getEnclosingClass() == null)
return addTo; return addTo;
if (Modifier.isStatic(clz.getModifiers())) if (Modifier.isStatic(clz.getModifiers()))
return nestingForType(clz.getEnclosingClass(), addTo); return addTo;
return nestingForType(clz.getEnclosingClass(), addTo.pushInner()); return nestingForType(clz.getEnclosingClass(), addTo.pushInner());
} else if (type instanceof ParameterizedType) { } else if (type instanceof ParameterizedType) {
ParameterizedType t = (ParameterizedType)type; ParameterizedType t = (ParameterizedType)type;
if (t.getOwnerType() == null) if (t.getOwnerType() == null)
return addTo; return addTo;
if (t.getRawType() instanceof Class
&& Modifier.isStatic(((Class) t.getRawType()).getModifiers()))
return addTo;
return nestingForType(t.getOwnerType(), addTo.pushInner()); return nestingForType(t.getOwnerType(), addTo.pushInner());
} }
return addTo; return addTo;
@ -193,14 +196,18 @@ public final class AnnotatedTypeFactory {
if (!(type instanceof Class<?>)) if (!(type instanceof Class<?>))
throw new IllegalStateException("Can't compute owner"); throw new IllegalStateException("Can't compute owner");
Class<?> inner = (Class<?>)type; Class<?> nested = (Class<?>)type;
Class<?> owner = inner.getDeclaringClass(); Class<?> owner = nested.getDeclaringClass();
if (owner == null) // top-level, local or anonymous if (owner == null) // top-level, local or anonymous
return null; return null;
if (inner.isPrimitive() || inner == Void.TYPE) if (nested.isPrimitive() || nested == Void.TYPE)
return 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(); TypeAnnotation[]all = getTypeAnnotations();
List<TypeAnnotation> l = new ArrayList<>(all.length); List<TypeAnnotation> l = new ArrayList<>(all.length);
@ -445,7 +452,12 @@ public final class AnnotatedTypeFactory {
Type owner = getParameterizedType().getOwnerType(); Type owner = getParameterizedType().getOwnerType();
if (owner == null) if (owner == null)
return 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(); TypeAnnotation[]all = getTypeAnnotations();
List<TypeAnnotation> l = new ArrayList<>(all.length); List<TypeAnnotation> l = new ArrayList<>(all.length);

View file

@ -187,19 +187,17 @@ public final class TypeAnnotation {
return new LocationInfo(newDepth, res); 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) { * Pops a location matching {@code tag}, or returns {@code null}
LocationInfo l = this; * if no matching location was found.
int newDepth = l.depth; */
while(newDepth > 0 && l.locations[newDepth - 1].tag == tag) { public LocationInfo popLocation(byte tag) {
newDepth--; if (depth == 0 || locations[depth - 1].tag != tag) {
return null;
} }
if (newDepth != l.depth) { Location[] res = new Location[depth - 1];
Location[] res = new Location[newDepth]; System.arraycopy(locations, 0, res, 0, depth - 1);
System.arraycopy(this.locations, 0, res, 0, newDepth); return new LocationInfo(depth - 1, res);
return new LocationInfo(newDepth, res);
} else
return l;
} }
public TypeAnnotation[] filter(TypeAnnotation[] ta) { public TypeAnnotation[] filter(TypeAnnotation[] ta) {

View file

@ -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);
}
}
}

View file

@ -42,6 +42,9 @@ public class GetAnnotatedOwnerType<Dummy> {
public @TA("non-generic") GetAnnotatedOwnerTypeAuxilliary . @TB("non-generic") Inner nonGeneric; 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("generic") InnerGeneric<String> innerGeneric;
public @TA("non-generic") GetAnnotatedOwnerTypeAuxilliary . @TB("raw") InnerGeneric innerRaw; 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 Object anonymous = new Object() {};
public @TA("array") Dummy[] dummy; public @TA("array") Dummy[] dummy;
public @TA("wildcard") GetAnnotatedOwnerType<?> wildcard; public @TA("wildcard") GetAnnotatedOwnerType<?> wildcard;
@ -58,6 +61,9 @@ public class GetAnnotatedOwnerType<Dummy> {
testNonGeneric(); testNonGeneric();
testInnerGeneric(); testInnerGeneric();
testInnerRaw(); testInnerRaw();
testNestedNonGeneric();
testNestedGeneric();
testNestedRaw();
testLocalClass(); testLocalClass();
testAnonymousClass(); testAnonymousClass();
@ -155,6 +161,54 @@ public class GetAnnotatedOwnerType<Dummy> {
+ outer.getAnnotations().length); + 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 { public static void testLocalClass() throws Exception {
class ALocalClass {} class ALocalClass {}
class OneMore { class OneMore {
@ -279,4 +333,8 @@ class GetAnnotatedOwnerTypeAuxilliary {
class Inner {} class Inner {}
class InnerGeneric<Dummy> {} class InnerGeneric<Dummy> {}
static class Nested {}
static class NestedGeneric<Dummy> {}
} }