Browse Source

Avoid infinite recursion for self-referencing generic type

Closes gh-32282
See gh-30079
pull/32294/head
Juergen Hoeller 2 years ago
parent
commit
ea3573176a
  1. 28
      spring-core/src/main/java/org/springframework/core/ResolvableType.java
  2. 24
      spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java

28
spring-core/src/main/java/org/springframework/core/ResolvableType.java

@ -28,8 +28,10 @@ import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType; import java.lang.reflect.WildcardType;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.StringJoiner; import java.util.StringJoiner;
import org.springframework.core.SerializableTypeWrapper.FieldTypeProvider; import org.springframework.core.SerializableTypeWrapper.FieldTypeProvider;
@ -588,18 +590,28 @@ public class ResolvableType implements Serializable {
if (this == NONE) { if (this == NONE) {
return false; return false;
} }
return hasUnresolvableGenerics(null);
}
private boolean hasUnresolvableGenerics(@Nullable Set<Type> alreadySeen) {
Boolean unresolvableGenerics = this.unresolvableGenerics; Boolean unresolvableGenerics = this.unresolvableGenerics;
if (unresolvableGenerics == null) { if (unresolvableGenerics == null) {
unresolvableGenerics = determineUnresolvableGenerics(); unresolvableGenerics = determineUnresolvableGenerics(alreadySeen);
this.unresolvableGenerics = unresolvableGenerics; this.unresolvableGenerics = unresolvableGenerics;
} }
return unresolvableGenerics; return unresolvableGenerics;
} }
private boolean determineUnresolvableGenerics() { private boolean determineUnresolvableGenerics(@Nullable Set<Type> alreadySeen) {
if (alreadySeen != null && alreadySeen.contains(this.type)) {
// Self-referencing generic -> not unresolvable
return false;
}
ResolvableType[] generics = getGenerics(); ResolvableType[] generics = getGenerics();
for (ResolvableType generic : generics) { for (ResolvableType generic : generics) {
if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds() || generic.hasUnresolvableGenerics()) { if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds() ||
generic.hasUnresolvableGenerics(currentTypeSeen(alreadySeen))) {
return true; return true;
} }
} }
@ -619,12 +631,20 @@ public class ResolvableType implements Serializable {
} }
Class<?> superclass = resolved.getSuperclass(); Class<?> superclass = resolved.getSuperclass();
if (superclass != null && superclass != Object.class) { if (superclass != null && superclass != Object.class) {
return getSuperType().hasUnresolvableGenerics(); return getSuperType().hasUnresolvableGenerics(currentTypeSeen(alreadySeen));
} }
} }
return false; return false;
} }
private Set<Type> currentTypeSeen(@Nullable Set<Type> alreadySeen) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>(4);
}
alreadySeen.add(this.type);
return alreadySeen;
}
/** /**
* Determine whether the underlying type is a type variable that * Determine whether the underlying type is a type variable that
* cannot be resolved through the associated variable resolver. * cannot be resolved through the associated variable resolver.

24
spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java

@ -1321,6 +1321,18 @@ class ResolvableTypeTests {
assertThat(type.hasUnresolvableGenerics()).isTrue(); assertThat(type.hasUnresolvableGenerics()).isTrue();
} }
@Test
void hasUnresolvableGenericsWhenSelfReferring() {
ResolvableType type = ResolvableType.forInstance(new Bar());
assertThat(type.hasUnresolvableGenerics()).isFalse();
}
@Test
void hasUnresolvableGenericsWithEnum() {
ResolvableType type = ResolvableType.forType(SimpleEnum.class.getGenericSuperclass());
assertThat(type.hasUnresolvableGenerics()).isFalse();
}
@Test @Test
void spr11219() throws Exception { void spr11219() throws Exception {
ResolvableType type = ResolvableType.forField(BaseProvider.class.getField("stuff"), BaseProvider.class); ResolvableType type = ResolvableType.forField(BaseProvider.class.getField("stuff"), BaseProvider.class);
@ -1624,12 +1636,22 @@ class ResolvableTypeTests {
} }
public interface ListOfListSupplier<T> { interface ListOfListSupplier<T> {
List<List<T>> get(); List<List<T>> get();
}
class Foo<T extends Foo<T>> {
}
class Bar extends Foo<Bar> {
} }
enum SimpleEnum { VALUE }
static class EnclosedInParameterizedType<T> { static class EnclosedInParameterizedType<T> {
static class InnerRaw { static class InnerRaw {

Loading…
Cancel
Save