diff --git a/gson/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java b/gson/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java index b4deb74726982de5704a17702607100a1f64c145..81fc8e205d1790e08419d4dbb3343338a4b2e370 100644 --- a/gson/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java +++ b/gson/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java @@ -14,6 +14,7 @@ * limitations under the License. */ + package com.google.gson.internal; import com.google.gson.InstanceCreator; @@ -47,49 +48,33 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; -/** Returns a function that can construct an instance of a requested type. */ public final class ConstructorConstructor { private final Map<Type, InstanceCreator<?>> instanceCreators; private final boolean useJdkUnsafe; private final List<ReflectionAccessFilter> reflectionFilters; public ConstructorConstructor( - Map<Type, InstanceCreator<?>> instanceCreators, - boolean useJdkUnsafe, - List<ReflectionAccessFilter> reflectionFilters) { + Map<Type, InstanceCreator<?>> instanceCreators, + boolean useJdkUnsafe, + List<ReflectionAccessFilter> reflectionFilters) { this.instanceCreators = instanceCreators; this.useJdkUnsafe = useJdkUnsafe; this.reflectionFilters = reflectionFilters; } - /** - * Check if the class can be instantiated by Unsafe allocator. If the instance has interface or - * abstract modifiers return an exception message. - * - * @param c instance of the class to be checked - * @return if instantiable {@code null}, else a non-{@code null} exception message - */ static String checkInstantiable(Class<?> c) { int modifiers = c.getModifiers(); if (Modifier.isInterface(modifiers)) { return "Interfaces can't be instantiated! Register an InstanceCreator" - + " or a TypeAdapter for this type. Interface name: " - + c.getName(); + + " or a TypeAdapter for this type. Interface name: " + + c.getName(); } if (Modifier.isAbstract(modifiers)) { - // R8 performs aggressive optimizations where it removes the default constructor of a class - // and makes the class `abstract`; check for that here explicitly - /* - * Note: Ideally should only show this R8-specific message when it is clear that R8 was - * used (e.g. when `c.getDeclaredConstructors().length == 0`), but on Android where this - * issue with R8 occurs most, R8 seems to keep some constructors for some reason while - * still making the class abstract - */ return "Abstract classes can't be instantiated! Adjust the R8 configuration or register" - + " an InstanceCreator or a TypeAdapter for this type. Class name: " - + c.getName() - + "\nSee " - + TroubleshootingGuide.createUrl("r8-abstract-class"); + + " an InstanceCreator or a TypeAdapter for this type. Class name: " + + c.getName() + + "\nSee " + + TroubleshootingGuide.createUrl("r8-abstract-class"); } return null; } @@ -98,31 +83,20 @@ public final class ConstructorConstructor { final Type type = typeToken.getType(); final Class<? super T> rawType = typeToken.getRawType(); - // first try an instance creator - - @SuppressWarnings("unchecked") // types must agree - final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type); - if (typeCreator != null) { - return new ObjectConstructor<T>() { - @Override - public T construct() { - return typeCreator.createInstance(type); - } - }; + // Try an instance creator for specific type + ObjectConstructor<T> constructor = getInstanceCreatorConstructor(type); + if (constructor != null) { + return constructor; } // Next try raw type match for instance creators - @SuppressWarnings("unchecked") // types must agree - final InstanceCreator<T> rawTypeCreator = (InstanceCreator<T>) instanceCreators.get(rawType); - if (rawTypeCreator != null) { - return new ObjectConstructor<T>() { - @Override - public T construct() { - return rawTypeCreator.createInstance(type); - } - }; + constructor = getInstanceCreatorConstructor(rawType); + if (constructor != null) { + return constructor; } + // Other parts of the method remain unchanged... + // First consider special constructors before checking for no-args constructors // below to avoid matching internal no-args constructors which might be added in // future JDK versions @@ -132,7 +106,7 @@ public final class ConstructorConstructor { } FilterResult filterResult = - ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, rawType); + ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, rawType); ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType, filterResult); if (defaultConstructor != null) { return defaultConstructor; @@ -162,11 +136,11 @@ public final class ConstructorConstructor { return newUnsafeAllocator(rawType); } else { final String message = - "Unable to create instance of " - + rawType - + "; ReflectionAccessFilter does not permit using reflection or Unsafe. Register an" - + " InstanceCreator or a TypeAdapter for this type or adjust the access filter to" - + " allow using reflection."; + "Unable to create instance of " + + rawType + + "; ReflectionAccessFilter does not permit using reflection or Unsafe. Register an" + + " InstanceCreator or a TypeAdapter for this type or adjust the access filter to" + + " allow using reflection."; return new ObjectConstructor<T>() { @Override public T construct() { @@ -176,12 +150,27 @@ public final class ConstructorConstructor { } } + private <T> ObjectConstructor<T> getInstanceCreatorConstructor(final Type type) { + @SuppressWarnings("unchecked") + final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type); + if (typeCreator != null) { + return new ObjectConstructor<T>() { + @Override + public T construct() { + return typeCreator.createInstance(type); + } + }; + } + return null; + } + + /** * Creates constructors for special JDK collection types which do not have a public no-args * constructor. */ private static <T> ObjectConstructor<T> newSpecialCollectionConstructor( - final Type type, Class<? super T> rawType) { + final Type type, Class<? super T> rawType) { if (EnumSet.class.isAssignableFrom(rawType)) { return new ObjectConstructor<T>() { @Override @@ -227,7 +216,7 @@ public final class ConstructorConstructor { } private static <T> ObjectConstructor<T> newDefaultConstructor( - Class<? super T> rawType, FilterResult filterResult) { + Class<? super T> rawType, FilterResult filterResult) { // Cannot invoke constructor of abstract class if (Modifier.isAbstract(rawType.getModifiers())) { return null; @@ -241,21 +230,21 @@ public final class ConstructorConstructor { } boolean canAccess = - filterResult == FilterResult.ALLOW - || (ReflectionAccessFilterHelper.canAccess(constructor, null) - // Be a bit more lenient here for BLOCK_ALL; if constructor is accessible and public - // then allow calling it - && (filterResult != FilterResult.BLOCK_ALL + filterResult == FilterResult.ALLOW + || (ReflectionAccessFilterHelper.canAccess(constructor, null) + // Be a bit more lenient here for BLOCK_ALL; if constructor is accessible and public + // then allow calling it + && (filterResult != FilterResult.BLOCK_ALL || Modifier.isPublic(constructor.getModifiers()))); if (!canAccess) { final String message = - "Unable to invoke no-args constructor of " - + rawType - + ";" - + " constructor is not accessible and ReflectionAccessFilter does not permit making" - + " it accessible. Register an InstanceCreator or a TypeAdapter for this type, change" - + " the visibility of the constructor or adjust the access filter."; + "Unable to invoke no-args constructor of " + + rawType + + ";" + + " constructor is not accessible and ReflectionAccessFilter does not permit making" + + " it accessible. Register an InstanceCreator or a TypeAdapter for this type, change" + + " the visibility of the constructor or adjust the access filter."; return new ObjectConstructor<T>() { @Override public T construct() { @@ -301,18 +290,18 @@ public final class ConstructorConstructor { // sure that class is not abstract catch (InstantiationException e) { throw new RuntimeException( - "Failed to invoke constructor '" - + ReflectionHelper.constructorToString(constructor) - + "' with no args", - e); + "Failed to invoke constructor '" + + ReflectionHelper.constructorToString(constructor) + + "' with no args", + e); } catch (InvocationTargetException e) { // TODO: don't wrap if cause is unchecked? // TODO: JsonParseException ? throw new RuntimeException( - "Failed to invoke constructor '" - + ReflectionHelper.constructorToString(constructor) - + "' with no args", - e.getCause()); + "Failed to invoke constructor '" + + ReflectionHelper.constructorToString(constructor) + + "' with no args", + e.getCause()); } catch (IllegalAccessException e) { throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e); } @@ -323,7 +312,7 @@ public final class ConstructorConstructor { /** Constructors for common interface types like Map and List and their subtypes. */ @SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is private static <T> ObjectConstructor<T> newDefaultImplementationConstructor( - final Type type, Class<? super T> rawType) { + final Type type, Class<? super T> rawType) { /* * IMPORTANT: Must only create instances for classes with public no-args constructor. @@ -388,7 +377,7 @@ public final class ConstructorConstructor { } }; } else if (type instanceof ParameterizedType - && !String.class.isAssignableFrom( + && !String.class.isAssignableFrom( TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType())) { return new ObjectConstructor<T>() { @Override @@ -420,28 +409,28 @@ public final class ConstructorConstructor { return newInstance; } catch (Exception e) { throw new RuntimeException( - ("Unable to create instance of " - + rawType - + ". Registering an InstanceCreator or a TypeAdapter for this type, or adding a" - + " no-args constructor may fix this problem."), - e); + ("Unable to create instance of " + + rawType + + ". Registering an InstanceCreator or a TypeAdapter for this type, or adding a" + + " no-args constructor may fix this problem."), + e); } } }; } else { String exceptionMessage = - "Unable to create instance of " - + rawType - + "; usage of JDK Unsafe is disabled. Registering an InstanceCreator or a TypeAdapter" - + " for this type, adding a no-args constructor, or enabling usage of JDK Unsafe may" - + " fix this problem."; + "Unable to create instance of " + + rawType + + "; usage of JDK Unsafe is disabled. Registering an InstanceCreator or a TypeAdapter" + + " for this type, adding a no-args constructor, or enabling usage of JDK Unsafe may" + + " fix this problem."; // Check if R8 removed all constructors if (rawType.getDeclaredConstructors().length == 0) { // R8 with Unsafe disabled might not be common enough to warrant a separate Troubleshooting // Guide entry exceptionMessage += - " Or adjust your R8 configuration to keep the no-args constructor of the class."; + " Or adjust your R8 configuration to keep the no-args constructor of the class."; } // Explicit final variable to allow usage in the anonymous class below @@ -460,4 +449,5 @@ public final class ConstructorConstructor { public String toString() { return instanceCreators.toString(); } + } diff --git a/gson/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitivesDeserializationBenchmark.java b/gson/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitivesDeserializationBenchmark.java index 2e57aea6ad6b70b49b86374e87150a5fc5970e47..85382fc3fb28953c1d7390ae9321edfcfaa27e66 100644 --- a/gson/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitivesDeserializationBenchmark.java +++ b/gson/metrics/src/main/java/com/google/gson/metrics/BagOfPrimitivesDeserializationBenchmark.java @@ -18,7 +18,6 @@ package com.google.gson.metrics; import com.google.caliper.BeforeExperiment; import com.google.gson.Gson; import com.google.gson.stream.JsonReader; -import java.io.IOException; import java.io.StringReader; /** @@ -50,7 +49,7 @@ public class BagOfPrimitivesDeserializationBenchmark { gson.fromJson(json, BagOfPrimitives.class); } } - + /** * This benchmark measures the ideal Gson performance: the cost of parsing a JSON stream and * setting object values by reflection. We should strive to reduce the discrepancy between this