8198526: getAnnotatedOwnerType does not handle static nested classes correctly
Reviewed-by: jfranck
This commit is contained in:
parent
265abce7fb
commit
1cd847d6d1
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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> {}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user