Fix NullPointerException introduced by covariant array fix. Happens when a root type is an array whose leaf type is a type parameter. Review by: mdempsky@google.com git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@11380 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java index 8c8f90f..6900fcf 100644 --- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java +++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -1541,14 +1541,29 @@ * the given array's leaf type and its instantiable subtypes. (Note: this adds O(S * R) * array types to the output where S is the number of subtypes and R is the rank.) * Prerequisite: The leaf type's tic and its subtypes must already be created. + * @see #checkArrayInstantiable */ private void markArrayTypes(TreeLogger logger, JArrayType array, TypePath path, ProblemReport problems) { logger = logger.branch(TreeLogger.DEBUG, "Adding array types for " + array); JType leafType = array.getLeafType(); + JTypeParameter isLeafTypeParameter = leafType.isTypeParameter(); + if (isLeafTypeParameter != null) { + if (typeParametersInRootTypes.contains(isLeafTypeParameter)) { + leafType = isLeafTypeParameter.getFirstBound(); // to match computeTypeInstantiability + } else { + // skip non-root leaf parameters, to match checkArrayInstantiable + return; + } + } + TypeInfoComputed leafTic = typeToTypeInfoComputed.get(leafType); - assert leafTic != null : "not computed: " + leafType; + if (leafTic == null) { + problems.add(array, "internal error: leaf type not computed: " + + leafType.getQualifiedSourceName(), Priority.FATAL); + return; + } JClassType leafClass = leafType.isClassOrInterface(); if (leafClass == null) {
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java index ee3fcd1..556326e 100644 --- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java +++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -50,6 +50,7 @@ import junit.framework.TestCase; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -1932,7 +1933,24 @@ */ public void testSubclassUsedInArray() throws NotFoundException, UnableToCompleteException { JClassType expected = arrayType(SubclassUsedInArray.LeafType.class); - checkSerializable(expected, SubclassUsedInArray.Base.class, SubclassUsedInArray.HasArray.class); + checkSerializable(expected, + eachJType(SubclassUsedInArray.Base.class, SubclassUsedInArray.HasArray.class)); + } + + /** + * Test the case where a root array type has a type parameter as its leaf. + */ + public void testArrayOfTypeParameterExtendsSubclass() throws Exception { + JClassType expected = arrayType(SubclassUsedInArray.LeafType.class); + + TypeOracle oracle = getTestTypeOracle(); + JGenericType genericHasArray = oracle + .getType(SubclassUsedInArray.GenericHasArray.class.getCanonicalName()).isGenericType(); + JTypeParameter typeParameter = genericHasArray.getTypeParameters()[0]; + + checkSerializable(expected, + oracle.getType(SubclassUsedInArray.Base.class.getCanonicalName()), + oracle.getArrayType(typeParameter)); } /** @@ -2038,7 +2056,7 @@ JClassType foo = to.getType("Foo"); JClassType bar = to.getType("Bar"); JClassType intfOfString = - to.getParameterizedType(intf, new JClassType[] {to.getType(String.class.getName())}); + to.getParameterizedType(intf, new JClassType[]{to.getType(String.class.getName())}); JClassType ser = to.getType("Ser"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); @@ -2312,7 +2330,7 @@ JTypeParameter typeParam = c.getTypeParameters()[0]; - JParameterizedType parameterizedType = to.getParameterizedType(a, new JClassType[] {typeParam}); + JParameterizedType parameterizedType = to.getParameterizedType(a, new JClassType[]{typeParam}); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, parameterizedType); SerializableTypeOracle so = sob.build(logger); @@ -2381,12 +2399,12 @@ * Checks that a type is serializable when searching from the given roots. * Also, check that the root order doesn't matter. */ - private void checkSerializable(JClassType expected, Class... roots) + private void checkSerializable(JClassType expected, JType... roots) throws UnableToCompleteException { + roots = Arrays.copyOf(roots, roots.length); // find serializable types in forward and reverse order SerializableTypeOracle forwardResult = findSerializable(roots); - roots = Arrays.copyOf(roots, roots.length); Collections.reverse(Arrays.asList(roots)); SerializableTypeOracle reverseResult = findSerializable(roots); @@ -2404,21 +2422,27 @@ checkSameSerializables(forwardResult, reverseResult); } - /** - * Finds serializable types from the given roots (in order). - */ - private SerializableTypeOracle findSerializable(Class... roots) + private SerializableTypeOracle findSerializable(JType... rootTypes) throws UnableToCompleteException { TreeLogger logger = createLogger(); TypeOracle oracle = getTestTypeOracle(); SerializableTypeOracleBuilder builder = createSerializableTypeOracleBuilder(logger, oracle); - for (Class root : roots) { - builder.addRootType(logger, oracle.findType(root.getCanonicalName())); + for (JType root : rootTypes) { + builder.addRootType(logger, root); } return builder.build(logger); } + private JType[] eachJType(Class... classes) throws UnableToCompleteException { + TypeOracle oracle = getTestTypeOracle(); + List<JType> result = new ArrayList<JType>(); + for (Class aClass : classes) { + result.add(oracle.findType(aClass.getCanonicalName())); + } + return result.toArray(new JType[result.size()]); + } + private void checkSameSerializables(SerializableTypeOracle first, SerializableTypeOracle second) { String firstTypes = join("\n", first.getSerializableTypes()); String secondTypes = join("\n", second.getSerializableTypes());
diff --git a/user/test/com/google/gwt/user/rebind/rpc/testcases/client/SubclassUsedInArray.java b/user/test/com/google/gwt/user/rebind/rpc/testcases/client/SubclassUsedInArray.java index 903e8d9..247875f 100644 --- a/user/test/com/google/gwt/user/rebind/rpc/testcases/client/SubclassUsedInArray.java +++ b/user/test/com/google/gwt/user/rebind/rpc/testcases/client/SubclassUsedInArray.java
@@ -42,6 +42,11 @@ private Subtype[] array; } + /** Used to create a type parameter with Subtype as its upper bound */ + public static class GenericHasArray<T extends Subtype> implements Serializable { + private T[] array; + } + /** Just a field. */ public static class FieldType implements Serializable { }