Removed use of a global table (typeIdArray) for testing castability between types.  This information is now stored per class prototype as a castableMap.

Review at http://gwt-code-reviews.appspot.com/750801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8548 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/CastableTypeMap.java b/dev/core/src/com/google/gwt/core/ext/linker/CastableTypeMap.java
new file mode 100644
index 0000000..62f0b50
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/CastableTypeMap.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.core.ext.linker;
+
+import java.io.Serializable;
+
+/**
+ * An encapsulation of a JSON map containing a set of castable type ids.
+ */
+public interface CastableTypeMap extends Serializable {
+  
+    /**
+     * Returns an eval-able JSON object.
+     */
+    String toJs();
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java b/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
index 5216fbf..c286a54 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
@@ -79,6 +79,11 @@
       return o1.getJsniIdent().compareTo(o2.getJsniIdent());
     }
   }
+  
+  /**
+   * Returns a JSON map of castableTypes
+   */
+  CastableTypeMap getCastableTypeMap();
 
   /**
    * Returns the name of the type or enclosing type if the symbol is a method or
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCastableTypeMap.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCastableTypeMap.java
new file mode 100644
index 0000000..0313d5d
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCastableTypeMap.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.core.ext.linker.impl;
+
+import com.google.gwt.core.ext.linker.CastableTypeMap;
+
+/**
+ * The standard implementation of {@link CastableTypeMap}.
+ */
+public class StandardCastableTypeMap implements CastableTypeMap {
+  
+  final String jsonData;
+  public StandardCastableTypeMap(String jsonData) {
+    this.jsonData = jsonData;
+  }
+
+  public String toJs() {
+    return jsonData;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
index 68ebda2..0688a03 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.core.ext.linker.impl;
 
+import com.google.gwt.core.ext.linker.CastableTypeMap;
 import com.google.gwt.core.ext.linker.SymbolData;
 
 import java.io.File;
@@ -31,15 +32,15 @@
 public class StandardSymbolData implements SymbolData {
 
   public static StandardSymbolData forClass(String className, String uriString,
-      int lineNumber, int typeId) {
+      int lineNumber, int typeId, CastableTypeMap castableTypeMap) {
     return new StandardSymbolData(className, null, null, uriString, lineNumber,
-        typeId);
+        typeId, castableTypeMap);
   }
 
   public static StandardSymbolData forMember(String className,
       String memberName, String methodSig, String uriString, int lineNumber) {
     return new StandardSymbolData(className, memberName, methodSig, uriString,
-        lineNumber, 0);
+        lineNumber, 0, null);
   }
 
   public static String toUriString(String fileName) {
@@ -62,9 +63,11 @@
   private String sourceUri;
   private String symbolName;
   private int typeId;
+  private CastableTypeMap castableTypeMap;
 
   private StandardSymbolData(String className, String memberName,
-      String methodSig, String sourceUri, int sourceLine, int typeId) {
+      String methodSig, String sourceUri, int sourceLine, int typeId, 
+      CastableTypeMap castableTypeMap) {
     assert className != null && className.length() > 0 : "className";
     assert memberName != null || methodSig == null : "methodSig without memberName";
     assert sourceLine >= -1 : "sourceLine: " + sourceLine;
@@ -75,6 +78,11 @@
     this.sourceUri = sourceUri;
     this.sourceLine = sourceLine;
     this.typeId = typeId;
+    this.castableTypeMap = castableTypeMap;
+  }
+  
+  public CastableTypeMap getCastableTypeMap() {
+    return castableTypeMap;
   }
 
   public String getClassName() {
@@ -152,6 +160,7 @@
     sourceUri = (String) in.readObject();
     symbolName = in.readUTF();
     typeId = in.readInt();
+    castableTypeMap = (CastableTypeMap) in.readObject();
   }
 
   /**
@@ -179,5 +188,6 @@
     out.writeObject(sourceUri);
     out.writeUTF(symbolName);
     out.writeInt(typeId);
+    out.writeObject(castableTypeMap);
   }
 }
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 0e85647..397f6f5 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
@@ -318,7 +318,7 @@
    */
   private final SourceInfo intrinsic;
 
-  private List<JsonObject> jsonTypeTable;
+  private List<JsonObject> jsonCastableTypeMaps;
 
   private Map<JReferenceType, JNonNullType> nonNullTypes = new IdentityHashMap<JReferenceType, JNonNullType>();
 
@@ -766,6 +766,20 @@
     return allEntryMethods;
   }
 
+  public JsonObject getCastableTypeMap(int typeId) {
+    // ensure jsonCastableTypeMaps has been initialized
+    // it might not have been if the CastNormalizer has not been run
+    if (jsonCastableTypeMaps == null) {
+      jsonCastableTypeMaps = new ArrayList<JsonObject>();
+      // ensure the always-false (typeId == 0) entry is present
+      jsonCastableTypeMaps.add(new JsonObject(createSourceInfoSynthetic(
+          JProgram.class, "always-false typeinfo entry"),
+          getJavaScriptObject()));
+    }
+    
+    return jsonCastableTypeMaps.get(typeId); 
+  }
+
   public CorrelationFactory getCorrelator() {
     return correlator;
   }
@@ -827,10 +841,6 @@
     return typeSpecialJavaScriptObject;
   }
 
-  public List<JsonObject> getJsonTypeTable() {
-    return jsonTypeTable;
-  }
-
   public JExpression getLiteralAbsentArrayDimension() {
     return JAbsentArrayDimension.INSTANCE;
   }
@@ -1161,7 +1171,7 @@
     for (int i = 0, c = types.size(); i < c; ++i) {
       typeIdMap.put(types.get(i), Integer.valueOf(i));
     }
-    this.jsonTypeTable = jsonObjects;
+    this.jsonCastableTypeMaps = jsonObjects;
   }
 
   public boolean isJavaLangString(JType type) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
index 14f1f2a..20b2993 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
@@ -35,6 +35,7 @@
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.js.JsonArray;
+import com.google.gwt.dev.jjs.ast.js.JsonObject;
 
 /**
  * Replace array accesses and instantiations with calls to the Array class.
@@ -123,11 +124,15 @@
       JMethodCall call = new JMethodCall(x.getSourceInfo(), null, initDim,
           arrayType);
       JLiteral classLit = x.getClassLiteral();
-      JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(arrayType));
+      
+      int typeId = program.getTypeId(arrayType);
+      JLiteral typeIdLit = program.getLiteralInt(typeId);
+      JsonObject castableTypeMap = program.getCastableTypeMap(typeId);
+      
       JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(arrayType));
       JExpression dim = x.dims.get(0);
       JType elementType = arrayType.getElementType();
-      call.addArgs(classLit, typeIdLit, queryIdLit, dim,
+      call.addArgs(classLit, typeIdLit, castableTypeMap, queryIdLit, dim,
           getSeedTypeLiteralFor(elementType));
       ctx.replaceMe(call);
     }
@@ -142,6 +147,8 @@
           program.getJavaScriptObject());
       JsonArray typeIdList = new JsonArray(sourceInfo,
           program.getJavaScriptObject());
+      JsonArray castableTypeMapList = new JsonArray(sourceInfo,
+          program.getJavaScriptObject());
       JsonArray queryIdList = new JsonArray(sourceInfo,
           program.getJavaScriptObject());
       JsonArray dimList = new JsonArray(sourceInfo,
@@ -154,8 +161,12 @@
         JLiteral classLit = x.getClassLiterals().get(i);
         classLitList.exprs.add(classLit);
 
-        JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(curArrayType));
+        int typeId = program.getTypeId(curArrayType);
+        JLiteral typeIdLit = program.getLiteralInt(typeId);
         typeIdList.exprs.add(typeIdLit);
+        
+        JsonObject castableTypeMap = program.getCastableTypeMap(typeId);
+        castableTypeMapList.exprs.add(castableTypeMap);
 
         JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(curArrayType));
         queryIdList.exprs.add(queryIdLit);
@@ -163,8 +174,8 @@
         dimList.exprs.add(x.dims.get(i));
         cur = curArrayType.getElementType();
       }
-      call.addArgs(classLitList, typeIdList, queryIdList, dimList,
-          program.getLiteralInt(dims), getSeedTypeLiteralFor(cur));
+      call.addArgs(classLitList, typeIdList, castableTypeMapList, queryIdList, 
+          dimList, program.getLiteralInt(dims), getSeedTypeLiteralFor(cur));
       ctx.replaceMe(call);
     }
 
@@ -176,14 +187,18 @@
       JMethodCall call = new JMethodCall(sourceInfo, null, initValues,
           arrayType);
       JLiteral classLit = x.getClassLiteral();
-      JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(arrayType));
+      
+      int typeId = program.getTypeId(arrayType);
+      JLiteral typeIdLit = program.getLiteralInt(typeId);
+      JsonObject castableTypeMap = program.getCastableTypeMap(typeId);
+      
       JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(arrayType));
       JsonArray initList = new JsonArray(sourceInfo,
           program.getJavaScriptObject());
       for (int i = 0; i < x.initializers.size(); ++i) {
         initList.exprs.add(x.initializers.get(i));
       }
-      call.addArgs(classLit, typeIdLit, queryIdLit, initList);
+      call.addArgs(classLit, typeIdLit, castableTypeMap, queryIdLit, initList);
       ctx.replaceMe(call);
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index e1d1910..0d2b50f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -16,6 +16,8 @@
 package com.google.gwt.dev.jjs.impl;
 
 import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.linker.CastableTypeMap;
+import com.google.gwt.core.ext.linker.impl.StandardCastableTypeMap;
 import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
 import com.google.gwt.dev.jjs.HasSourceInfo;
 import com.google.gwt.dev.jjs.InternalCompilerException;
@@ -444,9 +446,13 @@
 
     private void recordSymbol(JReferenceType x, JsName jsName) {
       int typeId = program.getTypeId(x);
+      
+      JsonObject castableTypeMapObj = program.getCastableTypeMap(typeId);
+      CastableTypeMap castableTypeMap = new StandardCastableTypeMap(castableTypeMapObj.toString());
+      
       StandardSymbolData symbolData = StandardSymbolData.forClass(x.getName(),
           x.getSourceInfo().getFileName(), x.getSourceInfo().getStartLine(),
-          typeId);
+          typeId, castableTypeMap);
       assert !symbolTable.containsKey(symbolData);
       symbolTable.put(symbolData, jsName);
     }
@@ -766,9 +772,6 @@
       if (x.getLiteralInitializer() != null) {
         // setup the constant value
         accept(x.getLiteralInitializer());
-      } else if (x == program.getIndexedField("Cast.typeIdArray")) {
-        // magic: setup the type id table
-        push(generateTypeTable());
       } else if (!x.hasInitializer()
           && x.getEnclosingType() != program.getTypeJavaLangObject()) {
         // setup a default value
@@ -1496,7 +1499,36 @@
       return new JsBinaryOperation(lhs.getSourceInfo(), JsBinaryOperator.COMMA,
           lhs, rhs);
     }
-
+    
+    private void generateCastableTypeIds(JClassType x, List<JsStatement> globalStmts) {
+      int typeId = program.getTypeId(x);
+      if (typeId >= 0) {
+        JField castableTypeMapField = program.getIndexedField("Object.castableTypeMap");
+        JsName castableTypeMapName = names.get(castableTypeMapField);
+        if (castableTypeMapName == null) {
+          // Was pruned; this compilation must have no dynamic casts.
+          return;
+        }
+        
+        SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic(
+              GenerateJavaScriptAST.class, "Castable type map");
+        
+        JsonObject jsonObject = program.getCastableTypeMap(typeId);
+        accept(jsonObject);
+        JsExpression objExpr = pop();
+        
+        // Generate castableTypeMap for each type prototype
+        // _.castableTypeMap$ = {2:1, 4:1, 12:1};
+        JsNameRef fieldRef = castableTypeMapName.makeRef(sourceInfo);
+        fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
+        JsExpression asg = createAssignment(fieldRef, objExpr);
+        
+        JsExprStmt asgStmt = asg.makeStmt();
+        globalStmts.add(asgStmt);
+        typeForStatMap.put(asgStmt, x);
+      }   
+    }
+    
     private void generateClassLiteral(JDeclarationStatement decl, JsVars vars) {
       JField field = (JField) decl.getVariableRef().getTarget();
       JsName jsName = names.get(field);
@@ -1533,6 +1565,7 @@
       }
 
       generateTypeId(x, globalStmts);
+      generateCastableTypeIds(x, globalStmts);
     }
 
     private void generateGwtOnLoad(List<JsFunction> entryFuncs,
@@ -1793,19 +1826,7 @@
       globalStmts.add(stmt);
       typeForStatMap.put(stmt, program.getTypeJavaLangObject());
     }
-
-    private JsExpression generateTypeTable() {
-      JsArrayLiteral arrayLit = new JsArrayLiteral(
-          jsProgram.createSourceInfoSynthetic(GenerateJavaScriptAST.class,
-              "Type table"));
-      for (int i = 0; i < program.getJsonTypeTable().size(); ++i) {
-        JsonObject jsonObject = program.getJsonTypeTable().get(i);
-        accept(jsonObject);
-        arrayLit.getExpressions().add((JsExpression) pop());
-      }
-      return arrayLit;
-    }
-
+    
     private void generateVTables(JClassType x, List<JsStatement> globalStmts) {
       for (JMethod method : x.getMethods()) {
         SourceInfo sourceInfo = method.getSourceInfo().makeChild(
@@ -2137,6 +2158,7 @@
     specialObfuscatedIdents.put("expando", "eX");
     specialObfuscatedIdents.put("typeId", "tI");
     specialObfuscatedIdents.put("typeMarker", "tM");
+    specialObfuscatedIdents.put("castableTypeMap", "cM");
 
     // String polymorphic
     specialObfuscatedIdents.put("charAt", "cA");
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
index 832dc9b..89a5246 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.lang;
 
+import com.google.gwt.core.client.JavaScriptObject;
+
 /**
  * This is a magic class the compiler uses as a base class for injected array
  * classes.
@@ -95,7 +97,8 @@
   public static <T> T[] cloneSubrange(T[] array, int fromIndex, int toIndex) {
     Array a = asArrayType(array);
     Array result = arraySlice(a, fromIndex, toIndex);
-    initValues(a.getClass(), Util.getTypeId(a), a.queryId, result);
+    initValues(a.getClass(), Util.getTypeId(a), 
+        Util.getCastableTypeMap(a), a.queryId, result);
     // implicit type arg not inferred (as of JDK 1.5.0_07)
     return Array.<T> asArray(result);
   }
@@ -114,7 +117,8 @@
   public static <T> T[] createFrom(T[] array, int length) {
     Array a = asArrayType(array);
     Array result = createFromSeed(NULL_SEED_TYPE, length);
-    initValues(a.getClass(), Util.getTypeId(a), a.queryId, result);
+    initValues(a.getClass(), Util.getTypeId(a), 
+        Util.getCastableTypeMap(a), a.queryId, result);
     // implicit type arg not inferred (as of JDK 1.5.0_07)
     return Array.<T> asArray(result);
   }
@@ -125,15 +129,17 @@
    * 
    * @param arrayClass the class of the array
    * @param typeId the typeId of the array
+   * @param castableTypeMap the map of types to which this array can be casted,
+   *          in the form of a JSON map object
    * @param queryId the queryId of the array
    * @param length the length of the array
    * @param seedType the primitive type of the array; 0: null; 1: zero; 2: false
    * @return the new array
    */
-  public static Array initDim(Class<?> arrayClass, int typeId, int queryId,
-      int length, int seedType) {
+  public static Array initDim(Class<?> arrayClass, int typeId, 
+        JavaScriptObject castableTypeMap, int queryId, int length, int seedType) {
     Array result = createFromSeed(seedType, length);
-    initValues(arrayClass, typeId, queryId, result);
+    initValues(arrayClass, typeId, castableTypeMap, queryId, result);
     return result;
   }
 
@@ -142,16 +148,19 @@
    * array, [a, b, c].
    * 
    * @param arrayClasses the class of each dimension of the array
-   * @param typeIdExprs the typeId at each dimension, from highest to lowest
-   * @param queryIdExprs the queryId at each dimension, from highest to lowest
-   * @param dimExprs the length at each dimension, from highest to lower
+   * @param typeIdExprs the typeId of each dimension, from highest to lowest
+   * @param castableTypeMapExprs the JSON castableTypeMap of each dimension,
+   *          from highest to lowest
+   * @param queryIdExprs the queryId of each dimension, from highest to lowest
+   * @param dimExprs the length of each dimension, from highest to lower
    * @param seedType the primitive type of the array; 0: null; 1: zero; 2: false
    * @return the new array
    */
   public static Array initDims(Class<?> arrayClasses[], int[] typeIdExprs,
-      int[] queryIdExprs, int[] dimExprs, int count, int seedType) {
-    return initDims(arrayClasses, typeIdExprs, queryIdExprs, dimExprs, 0,
-        count, seedType);
+      JavaScriptObject[] castableTypeMapExprs, int[] queryIdExprs, int[] dimExprs, 
+      int count, int seedType) {
+    return initDims(arrayClasses, typeIdExprs, castableTypeMapExprs, queryIdExprs, 
+        dimExprs, 0, count, seedType);
   }
 
   /**
@@ -160,15 +169,18 @@
    * 
    * @param arrayClass the class of the array
    * @param typeId the typeId of the array
+   * @param castableTypeMap the map of types to which this array can be casted,
+   *          in the form of a JSON map object
    * @param queryId the queryId of the array
    * @param array the JSON array that will be transformed into a GWT array
    * @return values; having wrapped it for GWT
    */
-  public static Array initValues(Class<?> arrayClass, int typeId, int queryId,
-      Array array) {
+  public static Array initValues(Class<?> arrayClass, int typeId, 
+      JavaScriptObject castableTypeMap, int queryId, Array array) {
     ExpandoWrapper.wrapArray(array);
     array.arrayClass = arrayClass;
     Util.setTypeId(array, typeId);
+    Util.setCastableTypeMap(array, castableTypeMap);
     array.queryId = queryId;
     return array;
   }
@@ -178,7 +190,7 @@
    */
   public static Object setCheck(Array array, int index, Object value) {
     if (value != null) {
-      if (array.queryId > 0 && !Cast.canCastUnsafe(Util.getTypeId(value), array.queryId)) {
+      if (array.queryId > 0 && !Cast.canCastUnsafe(value, array.queryId)) {
         throw new ArrayStoreException();
       }
       if (array.queryId < 0 && Cast.isJavaObject(value)) {
@@ -236,19 +248,22 @@
   }-*/;
 
   private static Array initDims(Class<?> arrayClasses[], int[] typeIdExprs,
-      int[] queryIdExprs, int[] dimExprs, int index, int count, int seedType) {
+      JavaScriptObject[] castableTypeMapExprs, int[] queryIdExprs, int[] dimExprs, 
+      int index, int count, int seedType) {
+    
     int length = dimExprs[index];
     boolean isLastDim = (index == (count - 1));
 
     Array result = createFromSeed(isLastDim ? seedType : NULL_SEED_TYPE, length);
-    initValues(arrayClasses[index], typeIdExprs[index], queryIdExprs[index], result);
+    initValues(arrayClasses[index], typeIdExprs[index], 
+        castableTypeMapExprs[index], queryIdExprs[index], result);
 
     if (!isLastDim) {
       // Recurse to next dimension.
       ++index;
       for (int i = 0; i < length; ++i) {
-        set(result, i, initDims(arrayClasses, typeIdExprs, queryIdExprs, dimExprs,
-            index, count, seedType));
+        set(result, i, initDims(arrayClasses, typeIdExprs, castableTypeMapExprs,
+            queryIdExprs, dimExprs, index, count, seedType));
       }
     }
     return result;
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
index 6310a50..ff10bf4 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
@@ -25,19 +25,18 @@
  */
 final class Cast {
 
-  // magic magic magic
-  protected static Object typeIdArray;
-
-  static native boolean canCast(int srcId, int dstId) /*-{
-    return srcId && !!@com.google.gwt.lang.Cast::typeIdArray[srcId][dstId];
+  static native boolean canCast(Object src, int dstId) /*-{
+    return src.@java.lang.Object::typeId &&
+          !!src.@java.lang.Object::castableTypeMap[dstId];
   }-*/;
 
   /**
    * Danger: value not coerced to boolean; use the result only in a boolean
    * context.
    */
-  static native boolean canCastUnsafe(int srcId, int dstId) /*-{
-    return srcId && @com.google.gwt.lang.Cast::typeIdArray[srcId][dstId];
+  static native boolean canCastUnsafe(Object src, int dstId) /*-{
+    return src.@java.lang.Object::typeId && 
+          src.@java.lang.Object::castableTypeMap[dstId];
   }-*/;
 
   static native String charToString(char x) /*-{
@@ -45,7 +44,7 @@
   }-*/;
 
   static Object dynamicCast(Object src, int dstId) {
-    if (src != null && !canCastUnsafe(Util.getTypeId(src), dstId)) {
+    if (src != null && !canCastUnsafe(src, dstId)) {
       throw new ClassCastException();
     }
     return src;
@@ -56,7 +55,7 @@
    */
   static Object dynamicCastAllowJso(Object src, int dstId) {
     if (src != null && !isJavaScriptObject(src) &&
-        !canCastUnsafe(Util.getTypeId(src), dstId)) {
+        !canCastUnsafe(src, dstId)) {
       throw new ClassCastException();
     }
     return src;
@@ -73,7 +72,7 @@
   }
 
   static boolean instanceOf(Object src, int dstId) {
-    return (src != null) && canCast(Util.getTypeId(src), dstId);
+    return (src != null) && canCast(src, dstId);
   }
 
   /**
@@ -89,7 +88,7 @@
    */
   static boolean instanceOfOrJso(Object src, int dstId) {
     return (src != null) &&
-        (isJavaScriptObject(src) || canCast(Util.getTypeId(src), dstId));
+        (isJavaScriptObject(src) || canCast(src, dstId));
   }
 
   static boolean isJavaObject(Object src) {
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Util.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Util.java
index facd2b8..a705c12 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Util.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Util.java
@@ -22,6 +22,10 @@
  * fields.
  */
 final class Util {
+  
+  static native JavaScriptObject getCastableTypeMap(Object o) /*-{
+    return o.@java.lang.Object::castableTypeMap;
+  }-*/;
 
   static native int getTypeId(Object o) /*-{
     return o.@java.lang.Object::typeId;
@@ -31,6 +35,10 @@
     return o.@java.lang.Object::typeMarker;
   }-*/;
 
+  static native void setCastableTypeMap(Object o, JavaScriptObject castableTypeMap) /*-{
+    o.@java.lang.Object::castableTypeMap = castableTypeMap;
+  }-*/;
+
   static native void setTypeId(Object o, int typeId) /*-{
     o.@java.lang.Object::typeId = typeId;
   }-*/;
diff --git a/tools/api-checker/config/gwt20_21userApi.conf b/tools/api-checker/config/gwt20_21userApi.conf
index 5e6b26f..7d58b57 100644
--- a/tools/api-checker/config/gwt20_21userApi.conf
+++ b/tools/api-checker/config/gwt20_21userApi.conf
@@ -142,6 +142,11 @@
 # Array.length is now intrinsic to the compiler
 com.google.gwt.lang.Array::length MISSING
 
+# Array initialization modified to include castableTypeMap arg
+com.google.gwt.lang.Array::initDim(Ljava/lang/Class;IIII) MISSING
+com.google.gwt.lang.Array::initDims([Ljava/lang/Class;[I[I[III) MISSING
+com.google.gwt.lang.Array::initValues(Ljava/lang/Class;IILcom/google/gwt/lang/Array;) MISSING
+
 # Add DateTimeFormatInfo, PredefinedFormat overrides
 com.google.gwt.i18n.client.DateTimeFormat::DateTimeFormat(Ljava/lang/String;Lcom/google/gwt/i18n/client/constants/DateTimeConstants;) OVERLOADED_METHOD_CALL
 com.google.gwt.i18n.client.DateTimeFormat::getFormat(Ljava/lang/String;) OVERLOADED_METHOD_CALL
diff --git a/user/src/com/google/gwt/rpc/linker/CastableTypeDataImpl.java b/user/src/com/google/gwt/rpc/linker/CastableTypeDataImpl.java
new file mode 100644
index 0000000..b9e4c6e
--- /dev/null
+++ b/user/src/com/google/gwt/rpc/linker/CastableTypeDataImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.rpc.linker;
+
+import com.google.gwt.core.ext.linker.CastableTypeMap;
+import com.google.gwt.rpc.server.CastableTypeData;
+
+/**
+ * Implementation of a wrapped castableTypeMap.
+ */
+public class CastableTypeDataImpl implements CastableTypeData {
+  
+  private final CastableTypeMap castableTypeMap;
+  
+  public CastableTypeDataImpl(CastableTypeMap castableTypeMap) {
+    this.castableTypeMap = castableTypeMap;
+  }
+
+  public String toJs() {
+    return castableTypeMap.toJs();
+  }
+
+}
diff --git a/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java b/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
index 1727296..619f06c 100644
--- a/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
+++ b/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
@@ -71,7 +71,8 @@
         for (SymbolData symbolData : result.getSymbolMap()) {
           builder.add(symbolData.getSymbolName(), symbolData.getJsniIdent(),
               symbolData.getClassName(), symbolData.getMemberName(),
-              symbolData.getTypeId());
+              symbolData.getTypeId(), 
+              new CastableTypeDataImpl(symbolData.getCastableTypeMap()));
         }
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
diff --git a/user/src/com/google/gwt/rpc/server/CastableTypeData.java b/user/src/com/google/gwt/rpc/server/CastableTypeData.java
new file mode 100644
index 0000000..c2c2e63
--- /dev/null
+++ b/user/src/com/google/gwt/rpc/server/CastableTypeData.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.rpc.server;
+
+import java.io.Serializable;
+
+/**
+ * An encapsulation of a JSON object containing castable type information.
+ */
+public interface CastableTypeData extends Serializable {
+  
+    /**
+     * Returns an eval-able JSON object.
+     */
+    String toJs();
+}
diff --git a/user/src/com/google/gwt/rpc/server/ClientOracle.java b/user/src/com/google/gwt/rpc/server/ClientOracle.java
index e1ab08e..754dbd8 100644
--- a/user/src/com/google/gwt/rpc/server/ClientOracle.java
+++ b/user/src/com/google/gwt/rpc/server/ClientOracle.java
@@ -51,6 +51,11 @@
   public abstract String createUnusedIdent(String ident);
 
   /**
+   * Returns the Json castableType data for a given type.
+   */
+  public abstract CastableTypeData getCastableTypeData(Class<?> clazz);
+
+  /**
    * Given a base type and the unobfuscated field name, find the obfuscated name
    * for the field in the client. This will search superclasses as well for the
    * first matching field.
diff --git a/user/src/com/google/gwt/rpc/server/DelegatingClientOracle.java b/user/src/com/google/gwt/rpc/server/DelegatingClientOracle.java
index 9dd5e0d..3bfa131 100644
--- a/user/src/com/google/gwt/rpc/server/DelegatingClientOracle.java
+++ b/user/src/com/google/gwt/rpc/server/DelegatingClientOracle.java
@@ -44,6 +44,11 @@
   }
 
   @Override
+  public CastableTypeData getCastableTypeData(Class<?> clazz) {
+    return delegate.getCastableTypeData(clazz);
+  }
+  
+  @Override
   public String getFieldId(Class<?> clazz, String fieldName) {
     return delegate.getFieldId(clazz, fieldName);
   }
diff --git a/user/src/com/google/gwt/rpc/server/HostedModeClientOracle.java b/user/src/com/google/gwt/rpc/server/HostedModeClientOracle.java
index 968dd15..cdc05b3 100644
--- a/user/src/com/google/gwt/rpc/server/HostedModeClientOracle.java
+++ b/user/src/com/google/gwt/rpc/server/HostedModeClientOracle.java
@@ -57,6 +57,11 @@
   public String createUnusedIdent(String ident) {
     return unimplemented();
   }
+  
+  @Override
+  public CastableTypeData getCastableTypeData(Class<?> clazz) {
+    return unimplemented();
+  }
 
   /**
    * Unimplemented.
diff --git a/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java b/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
index 0fbbdfc..786d20f 100644
--- a/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
+++ b/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
@@ -52,10 +52,11 @@
     private WebModeClientOracle oracle = new WebModeClientOracle();
 
     public void add(String jsIdent, String jsniIdent, String className,
-        String memberName, int typeId) {
+        String memberName, int typeId, CastableTypeData castableTypeData) {
       oracle.idents.add(jsIdent);
       ClassData data = oracle.getClassData(className);
       data.typeId = typeId;
+      data.castableTypeData = castableTypeData;
       if (jsniIdent == null || jsniIdent.length() == 0) {
         data.typeName = className;
         data.seedName = jsIdent;
@@ -108,15 +109,16 @@
   }
 
   private static class ClassData implements Serializable {
-    private static final long serialVersionUID = 3L;
+    private static final long serialVersionUID = 4L;
 
+    public CastableTypeData castableTypeData;
     public final Map<String, String> fieldIdentsToNames = new HashMap<String, String>();
     public final Map<String, String> fieldNamesToIdents = new HashMap<String, String>();
     public final Map<String, String> methodJsniNamesToIdents = new HashMap<String, String>();
     public String seedName;
     public List<String> serializableFields = Collections.emptyList();
-    public String typeName;
     public int typeId;
+    public String typeName;
   }
 
   /**
@@ -230,6 +232,18 @@
     }
     return ident;
   }
+  
+  @Override
+  public CastableTypeData getCastableTypeData(Class<?> clazz) {
+    while (clazz != null) {
+      CastableTypeData toReturn = getCastableTypeData(canonicalName(clazz));
+      if (toReturn != null) {
+        return toReturn;
+      }
+      clazz = clazz.getSuperclass();
+    }
+    return null;
+  }
 
   @Override
   public String getFieldId(Class<?> clazz, String fieldName) {
@@ -395,6 +409,11 @@
       return clazz.getName();
     }
   }
+  
+  private CastableTypeData getCastableTypeData(String className) {
+    ClassData data = getClassData(className);
+    return data.castableTypeData;
+  }
 
   private ClassData getClassData(String className) {
     ClassData toReturn = classData.get(className);
diff --git a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
index 714e378..ff6a19a 100644
--- a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
+++ b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
@@ -533,7 +533,8 @@
 
       String initValuesId = clientOracle.getMethodId(
           "com.google.gwt.lang.Array", "initValues", "Ljava/lang/Class;", "I",
-          "I", "Lcom/google/gwt/lang/Array;");
+          "Lcom/google/gwt/core/client/JavaScriptObject;", "I", 
+          "Lcom/google/gwt/lang/Array;");
       assert initValuesId != null : "Could not find initValues";
 
       String classLitId = clientOracle.getFieldId(
@@ -546,12 +547,16 @@
       constructorFunctions.put(targetClass, functionName);
 
       /*
-       * Set the typeIds and queryIds to exact values, or fall back to acting
-       * like a plain Object[] array.
+       * Set the typeIds, castableTypeData, and queryIds to exact values, 
+       * or fall back to acting like a plain Object[] array.
        */
+      CastableTypeData castableTypeData;
       int typeId = clientOracle.getTypeId(targetClass);
-      if (typeId == 0) {
+      if (typeId != 0) {
+        castableTypeData = clientOracle.getCastableTypeData(targetClass);
+      } else {
         typeId = clientOracle.getTypeId(Object[].class);
+        castableTypeData = clientOracle.getCastableTypeData(Object[].class);
       }
 
       int queryId = clientOracle.getTypeId(x.getComponentType());
@@ -575,6 +580,8 @@
       comma();
       push(String.valueOf(typeId));
       comma();
+      push(castableTypeData.toJs());
+      comma();
       push(String.valueOf(queryId));
       comma();
       push(ident);
diff --git a/user/super/com/google/gwt/emul/java/lang/Object.java b/user/super/com/google/gwt/emul/java/lang/Object.java
index 4979a83..46aa0ae 100644
--- a/user/super/com/google/gwt/emul/java/lang/Object.java
+++ b/user/super/com/google/gwt/emul/java/lang/Object.java
@@ -41,6 +41,14 @@
    */
   @SuppressWarnings("unused")
   private transient int typeId;
+  
+  /**
+   * A JavaScript Json map for looking up castability between types.
+   * 
+   * @skip
+   */
+  @SuppressWarnings("unused")
+  private transient JavaScriptObject castableTypeMap;
 
   /**
    * magic magic magic.
@@ -48,7 +56,7 @@
    * @skip
    */
   @SuppressWarnings("unused")
-  private transient Object typeMarker;
+  private transient JavaScriptObject typeMarker;
 
   public boolean equals(Object other) {
     return this == other;