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)
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 @@ public final class AnnotatedTypeFactory {
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 @@ public final class AnnotatedTypeFactory {
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);

View File

@ -187,19 +187,17 @@ public final class TypeAnnotation {
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) {

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("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 @@ public class GetAnnotatedOwnerType<Dummy> {
testNonGeneric();
testInnerGeneric();
testInnerRaw();
testNestedNonGeneric();
testNestedGeneric();
testNestedRaw();
testLocalClass();
testAnonymousClass();
@ -155,6 +161,54 @@ public class GetAnnotatedOwnerType<Dummy> {
+ 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 GetAnnotatedOwnerTypeAuxilliary {
class Inner {}
class InnerGeneric<Dummy> {}
static class Nested {}
static class NestedGeneric<Dummy> {}
}