Fixes issue 1822.  Arrays now implement Serializable and Cloneable.

Review by: scottb


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7343 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index df289af..3092e21 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -71,10 +71,10 @@
 
   public static final Set<String> INDEX_TYPES_SET = new LinkedHashSet<String>(
       Arrays.asList(new String[] {
-          "java.lang.Object", "java.lang.String", "java.lang.Class",
-          "java.lang.CharSequence", "java.lang.Comparable", "java.lang.Enum",
-          "java.lang.Iterable", "java.util.Iterator",
-          "com.google.gwt.core.client.GWT",
+          "java.io.Serializable", "java.lang.Object", "java.lang.String",
+          "java.lang.Class", "java.lang.CharSequence", "java.lang.Cloneable",
+          "java.lang.Comparable", "java.lang.Enum", "java.lang.Iterable",
+          "java.util.Iterator", "com.google.gwt.core.client.GWT",
           "com.google.gwt.core.client.JavaScriptObject",
           "com.google.gwt.lang.ClassLiteralHolder",
           "com.google.gwt.core.client.RunAsyncCallback",
@@ -313,6 +313,10 @@
 
   private Map<JReferenceType, Integer> typeIdMap = new HashMap<JReferenceType, Integer>();
 
+  private JInterfaceType typeJavaIoSerializable;
+
+  private JInterfaceType typeJavaLangCloneable;
+
   private JClassType typeJavaLangEnum;
 
   private JClassType typeJavaLangObject;
@@ -334,10 +338,10 @@
   /**
    * Constructor.
    * 
-   * @param enableSourceInfoDescendants Controls whether or not SourceInfo nodes
-   *          created via the JProgram will record descendant information.
-   *          Enabling this feature will collect extra data during the
-   *          compilation cycle, but at a cost of memory and object allocations.
+   * @param correlator Controls whether or not SourceInfo nodes created via the
+   *          JProgram will record descendant information. Enabling this feature
+   *          will collect extra data during the compilation cycle, but at a
+   *          cost of memory and object allocations.
    */
   public JProgram(CorrelationFactory correlator) {
     super(correlator.makeSourceInfo(SourceOrigin.create(0,
@@ -467,6 +471,11 @@
 
     if (INDEX_TYPES_SET.contains(sname)) {
       indexedTypes.put(x.getShortName(), x);
+      if (sname.equals("java.lang.Cloneable")) {
+        typeJavaLangCloneable = x;
+      } else if (sname.equals("java.io.Serializable")) {
+        typeJavaIoSerializable = x;
+      }
     }
 
     return x;
@@ -720,6 +729,9 @@
         // unrelated
         return typeJavaLangObject;
 
+      } else if (greater == IS_ARRAY
+          && ((tLesser == typeJavaLangCloneable) || (tLesser == typeJavaIoSerializable))) {
+        return tLesser;
       } else {
 
         // unrelated: the best commonality between an interface and array, or
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index ebd7a0b..bfff3cb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -262,6 +262,9 @@
    */
   private final Map<JMethod, Map<JClassType, Set<JMethod>>> virtualUpRefMap = new IdentityHashMap<JMethod, Map<JClassType, Set<JMethod>>>();
 
+  private JDeclaredType javaIoSerializable;
+  private JDeclaredType javaLangCloneable;
+
   public JTypeOracle(JProgram program) {
     this.program = program;
   }
@@ -310,6 +313,12 @@
         }
       }
 
+      /*
+       * Warning: If this code is ever updated to consider casts of array types
+       * to interface types, then be sure to consider that casting an array to
+       * Serializable and Cloneable succeeds. Currently all casts of an array to
+       * an interface return true, which is overly conservative but is safe.
+       */
     } else if (type instanceof JClassType) {
 
       JClassType cType = (JClassType) type;
@@ -369,6 +378,10 @@
           }
         }
       }
+
+      if (qType == javaIoSerializable || qType == javaLangCloneable) {
+        return true;
+      }
     } else if (type instanceof JClassType) {
 
       JClassType cType = (JClassType) type;
@@ -402,6 +415,9 @@
 
   public void computeBeforeAST() {
     javaLangObject = program.getTypeJavaLangObject();
+    javaIoSerializable = program.getFromTypeMap(Serializable.class.getName());
+    javaLangCloneable = program.getFromTypeMap(Cloneable.class.getName());
+
     superClassMap.clear();
     subClassMap.clear();
     superInterfaceMap.clear();
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
index 38821ac..4bb4cb9 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
@@ -64,6 +64,8 @@
   private SourceInfo synthSource;
   private JReferenceType typeNull;
   private JTypeOracle typeOracle;
+  private JReferenceType intfSerializable;
+  private JReferenceType intfCloneable;
 
   public void testCanTheoreticallyCast() {
     assertFalse(typeOracle.canTheoreticallyCast(classBnn, typeNull));
@@ -90,6 +92,12 @@
 
     assertTrue(typeOracle.canTheoreticallyCast(intfIBase, classBase));
     assertFalse(typeOracle.canTheoreticallyCast(intfJ, classA));
+
+    assertTrue(typeOracle.canTheoreticallyCast(arrayOfA, intfSerializable));
+    assertTrue(typeOracle.canTheoreticallyCast(intfSerializable, arrayOfA));
+
+    assertTrue(typeOracle.canTheoreticallyCast(arrayOfA, intfCloneable));
+    assertTrue(typeOracle.canTheoreticallyCast(intfCloneable, arrayOfA));
   }
 
   public void testCanTriviallyCast() {
@@ -133,6 +141,12 @@
 
     assertTrue(typeOracle.canTriviallyCast(classJso, classJso1));
     assertTrue(typeOracle.canTriviallyCast(classJso, classJso1));
+    
+    assertTrue(typeOracle.canTriviallyCast(arrayOfA, intfSerializable));
+    assertFalse(typeOracle.canTriviallyCast(intfSerializable, arrayOfA));
+
+    assertTrue(typeOracle.canTriviallyCast(arrayOfA, intfCloneable));
+    assertFalse(typeOracle.canTriviallyCast(intfCloneable, arrayOfA));
 
     /*
      * Test that two types cannot both be trivially castable to each other,
@@ -172,6 +186,9 @@
 
     assertSame(classObject, generalizeTypes(intfI, arrayOfInt));
 
+    assertSame(intfSerializable, generalizeTypes(intfSerializable, arrayOfA));
+    assertSame(intfCloneable, generalizeTypes(intfCloneable, arrayOfA));
+
     for (JReferenceType type1 : severalTypes()) {
       for (JReferenceType type2 : severalTypes()) {
         JReferenceType generalized = generalizeTypes(type1, type2);
@@ -187,6 +204,8 @@
     assertSame(classB, program.strongerType(classB, classBase));
     assertSame(classB, program.strongerType(classBase, classB));
     assertSame(intfI, program.strongerType(intfI, intfJ));
+    assertSame(arrayOfA, program.strongerType(intfSerializable, arrayOfA));
+    assertSame(arrayOfA, program.strongerType(intfCloneable, arrayOfA));
   }
 
   @Override
@@ -220,6 +239,9 @@
     classJso = createClass("com.google.gwt.core.client.JavaScriptObject",
         classObject, false, false);
 
+    intfSerializable = createInterface("java.io.Serializable");
+    intfCloneable = createInterface("java.lang.Cloneable");
+
     intfIBase = createInterface("IBase");
 
     intfI = createInterface("I");
diff --git a/user/test/com/google/gwt/dev/jjs/test/ClassCastTest.java b/user/test/com/google/gwt/dev/jjs/test/ClassCastTest.java
index 79eabce..8cd751d 100644
--- a/user/test/com/google/gwt/dev/jjs/test/ClassCastTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/ClassCastTest.java
@@ -16,9 +16,12 @@
 package com.google.gwt.dev.jjs.test;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.Widget;
+
+import java.io.Serializable;
 
 /**
- * TODO: document me.
+ * Test type casts and <code>instanceof</code>.
  */
 @SuppressWarnings("unused")
 public class ClassCastTest extends GWTTestCase {
@@ -32,14 +35,27 @@
   static abstract class Food {
   }
 
-  private final Food foodItem = new Apple();
+  private volatile Object arrayOfInt = new int[3];
+  private volatile Object arrayOfWidget = new Widget[4];
+  private volatile Food foodItem = new Apple();
+  private volatile CanEatRaw rawFoodItem = new Apple();
 
-  private final CanEatRaw rawFoodItem = new Apple();
-
+  @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
   }
 
+  public void testArrayInterfaces() {
+    assertTrue(arrayOfInt instanceof Serializable);
+    assertTrue(arrayOfWidget instanceof Serializable);
+
+    assertTrue(arrayOfInt instanceof Cloneable);
+    assertTrue(arrayOfWidget instanceof Cloneable);
+
+    assertFalse(arrayOfInt instanceof Food);
+    assertFalse(arrayOfWidget instanceof Food);
+  }
+
   public void testBaseToInterface() {
     Apple apple = (Apple) foodItem;
   }