Avoid infinite recursion for self-referencing generic type
Closes gh-32282 See gh-30079
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user