From eb43b10f96db685cb6f8565d3a38d18677b971a8 Mon Sep 17 00:00:00 2001 From: Andrey Mogilev Date: Mon, 31 Jul 2017 23:51:05 +0700 Subject: [PATCH 1/2] Fix StackOverflowError on resolving types with TypeVariable recursion Sample failing code: private static class TestType { TestType superType; } ... new Gson().getAdapter(TestType.class); --- .../com/google/gson/internal/$Gson$Types.java | 23 ++++++++++++----- .../bind/RecursiveTypesResolveTest.java | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java index 673945314e..8aa0982369 100644 --- a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java +++ b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java @@ -334,10 +334,21 @@ public static Type[] getMapKeyAndValueTypes(Type context, Class contextRawTyp } public static Type resolve(Type context, Class contextRawType, Type toResolve) { + return resolve(context, contextRawType, toResolve, new HashSet()); + } + + private static Type resolve(Type context, Class contextRawType, Type toResolve, + Collection visitedTypeVariables) { // this implementation is made a little more complicated in an attempt to avoid object-creation while (true) { if (toResolve instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) toResolve; + if (visitedTypeVariables.contains(typeVariable)) { + // cannot reduce due to infinite recursion + return toResolve; + } else { + visitedTypeVariables.add(typeVariable); + } toResolve = resolveTypeVariable(context, contextRawType, typeVariable); if (toResolve == typeVariable) { return toResolve; @@ -346,7 +357,7 @@ public static Type resolve(Type context, Class contextRawType, Type toResolve } else if (toResolve instanceof Class && ((Class) toResolve).isArray()) { Class original = (Class) toResolve; Type componentType = original.getComponentType(); - Type newComponentType = resolve(context, contextRawType, componentType); + Type newComponentType = resolve(context, contextRawType, componentType, visitedTypeVariables); return componentType == newComponentType ? original : arrayOf(newComponentType); @@ -354,7 +365,7 @@ public static Type resolve(Type context, Class contextRawType, Type toResolve } else if (toResolve instanceof GenericArrayType) { GenericArrayType original = (GenericArrayType) toResolve; Type componentType = original.getGenericComponentType(); - Type newComponentType = resolve(context, contextRawType, componentType); + Type newComponentType = resolve(context, contextRawType, componentType, visitedTypeVariables); return componentType == newComponentType ? original : arrayOf(newComponentType); @@ -362,12 +373,12 @@ public static Type resolve(Type context, Class contextRawType, Type toResolve } else if (toResolve instanceof ParameterizedType) { ParameterizedType original = (ParameterizedType) toResolve; Type ownerType = original.getOwnerType(); - Type newOwnerType = resolve(context, contextRawType, ownerType); + Type newOwnerType = resolve(context, contextRawType, ownerType, visitedTypeVariables); boolean changed = newOwnerType != ownerType; Type[] args = original.getActualTypeArguments(); for (int t = 0, length = args.length; t < length; t++) { - Type resolvedTypeArgument = resolve(context, contextRawType, args[t]); + Type resolvedTypeArgument = resolve(context, contextRawType, args[t], visitedTypeVariables); if (resolvedTypeArgument != args[t]) { if (!changed) { args = args.clone(); @@ -387,12 +398,12 @@ public static Type resolve(Type context, Class contextRawType, Type toResolve Type[] originalUpperBound = original.getUpperBounds(); if (originalLowerBound.length == 1) { - Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]); + Type lowerBound = resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables); if (lowerBound != originalLowerBound[0]) { return supertypeOf(lowerBound); } } else if (originalUpperBound.length == 1) { - Type upperBound = resolve(context, contextRawType, originalUpperBound[0]); + Type upperBound = resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables); if (upperBound != originalUpperBound[0]) { return subtypeOf(upperBound); } diff --git a/gson/src/test/java/com/google/gson/internal/bind/RecursiveTypesResolveTest.java b/gson/src/test/java/com/google/gson/internal/bind/RecursiveTypesResolveTest.java index 4b4ce891f1..aaa577b537 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/RecursiveTypesResolveTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/RecursiveTypesResolveTest.java @@ -86,4 +86,29 @@ public void testSubSupertype() { assertEquals($Gson$Types.subtypeOf(Object.class), $Gson$Types.subtypeOf($Gson$Types.supertypeOf(Number.class))); } + + // + // tests for recursion while resolving type variables + // + + private static class TestType { + TestType superType; + } + + private static class TestType2 { + TestType2 superReversedType; + } + + public void testRecursiveTypeVariablesResolve1() throws Exception { + TypeAdapter adapter = new Gson().getAdapter(TestType.class); + assertNotNull(adapter); + } + + public void testRecursiveTypeVariablesResolve12() throws Exception { + TypeAdapter adapter = new Gson().getAdapter(TestType2.class); + assertNotNull(adapter); + } } + + + From ef9a3f6d79cfb9e54ccc0fd1157c0a53c05f975d Mon Sep 17 00:00:00 2001 From: Andrey Mogilev Date: Tue, 1 Aug 2017 00:16:34 +0700 Subject: [PATCH 2/2] fix build errors --- .../src/main/java/com/google/gson/internal/$Gson$Types.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java index 8aa0982369..f66ac157b1 100644 --- a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java +++ b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java @@ -25,11 +25,7 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Properties; +import java.util.*; import static com.google.gson.internal.$Gson$Preconditions.checkArgument; import static com.google.gson.internal.$Gson$Preconditions.checkNotNull;