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 {
   }