Simplify JArrayType handling; reduce serialization footprint.

http://gwt-code-reviews.appspot.com/1352803/show

Review by: cromwellian@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9711 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
index 7652920..0fe86e0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayType.java
@@ -22,24 +22,15 @@
  */
 public class JArrayType extends JReferenceType {
 
-  private static String calcName(JType leafType, int dims) {
-    String name = leafType.getName();
-    for (int i = 0; i < dims; ++i) {
-      name = name + "[]";
-    }
-    return name;
-  }
+  private transient int dims = 0;
+  private final JType elementType;
+  private transient JType leafType = null;
 
-  private int dims;
-  private JType elementType;
-  private JType leafType;
-
-  public JArrayType(JType elementType, JType leafType, int dims) {
-    super(leafType.getSourceInfo().makeChild(SourceOrigin.UNKNOWN), calcName(
-        leafType, dims));
+  public JArrayType(JType elementType) {
+    super(elementType.getSourceInfo().makeChild(SourceOrigin.UNKNOWN),
+        elementType.getName() + "[]");
+    assert !(elementType instanceof JNonNullType);
     this.elementType = elementType;
-    this.leafType = leafType;
-    this.dims = dims;
   }
 
   @Override
@@ -48,6 +39,12 @@
   }
 
   public int getDims() {
+    if (dims == 0) {
+      dims = 1;
+      if (elementType instanceof JArrayType) {
+        dims += ((JArrayType) elementType).getDims();
+      }
+    }
     return dims;
   }
 
@@ -57,23 +54,22 @@
 
   @Override
   public String getJavahSignatureName() {
-    String s = leafType.getJavahSignatureName();
-    for (int i = 0; i < dims; ++i) {
-      s = "_3" + s;
-    }
-    return s;
+    return "_3" + elementType.getJavahSignatureName();
   }
 
   @Override
   public String getJsniSignatureName() {
-    String s = leafType.getJsniSignatureName();
-    for (int i = 0; i < dims; ++i) {
-      s = "[" + s;
-    }
-    return s;
+    return "[" + elementType.getJsniSignatureName();
   }
 
   public JType getLeafType() {
+    if (leafType == null) {
+      if (elementType instanceof JArrayType) {
+        leafType = ((JArrayType) elementType).getLeafType();
+      } else {
+        leafType = elementType;
+      }
+    }
     return leafType;
   }
 
@@ -87,7 +83,7 @@
   }
 
   public boolean isFinal() {
-    return leafType.isFinal();
+    return elementType.isFinal();
   }
 
   public void traverse(JVisitor visitor, Context ctx) {
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 4945840..658fbd4 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
@@ -46,7 +46,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeSet;
 
 /**
  * Root for the AST representing an entire Java program.
@@ -317,16 +316,12 @@
   public final JTypeOracle typeOracle = new JTypeOracle(this);
 
   /**
-   * Sorted to avoid nondeterministic iteration.
-   */
-  private final Set<JArrayType> allArrayTypes = new TreeSet<JArrayType>(
-      ARRAYTYPE_COMPARATOR);
-
-  /**
    * Special serialization treatment.
    */
   private transient List<JDeclaredType> allTypes = new ArrayList<JDeclaredType>();
 
+  private final HashMap<JType, JArrayType> arrayTypes = new HashMap<JType, JArrayType>();
+
   private final Map<JType, JClassLiteral> classLiterals = new IdentityHashMap<JType, JClassLiteral>();
 
   /**
@@ -334,13 +329,6 @@
    */
   private final CorrelationFactory correlator;
 
-  /**
-   * Each entry is a HashMap(JType => JArrayType), arranged such that the number
-   * of dimensions is that index (plus one) at which the JArrayTypes having that
-   * number of dimensions resides.
-   */
-  private final ArrayList<HashMap<JType, JArrayType>> dimensions = new ArrayList<HashMap<JType, JArrayType>>();
-
   private final Map<String, JField> indexedFields = new HashMap<String, JField>();
 
   private final Map<String, JMethod> indexedMethods = new HashMap<String, JMethod>();
@@ -775,11 +763,14 @@
   }
 
   /**
-   * Returns a sorted set of array types, so the returned set can be iterated
+   * Returns a sorted list of array types, so the returned set can be iterated
    * over without introducing nondeterminism.
    */
-  public Set<JArrayType> getAllArrayTypes() {
-    return allArrayTypes;
+  public List<JArrayType> getAllArrayTypes() {
+    ArrayList<JArrayType> result = new ArrayList<JArrayType>(
+        arrayTypes.values());
+    Collections.sort(result, ARRAYTYPE_COMPARATOR);
+    return result;
   }
 
   public List<JMethod> getAllEntryMethods() {
@@ -981,7 +972,7 @@
   }
 
   public int getQueryId(JReferenceType elementType) {
-    assert (elementType == getRunTimeType(elementType));
+    assert (elementType == elementType.getUnderlyingType());
     Integer integer = queryIds.get(elementType);
     if (integer == null) {
       return 0;
@@ -994,25 +985,6 @@
     return runAsyncReplacements;
   }
 
-  /**
-   * A run-time type is a type at the granularity that GWT tests at run time.
-   * These include declared types, arrays of declared types, arrays of
-   * primitives, and null. This is also the granularity for the notion of
-   * instantiability recorded in {@link JTypeOracle}. This method returns the
-   * narrowest supertype of <code>type</code> that is a run-time type.
-   */
-  public JReferenceType getRunTimeType(JReferenceType type) {
-    type = type.getUnderlyingType();
-    if (type instanceof JArrayType) {
-      JArrayType typeArray = (JArrayType) type;
-      if (typeArray.getLeafType() instanceof JNonNullType) {
-        JNonNullType leafType = (JNonNullType) typeArray.getLeafType();
-        type = getTypeArray(leafType.getUnderlyingType(), typeArray.getDims());
-      }
-    }
-    return type;
-  }
-
   public List<Integer> getSplitPointInitialSequence() {
     return splitPointInitialSequence;
   }
@@ -1021,45 +993,26 @@
     return instanceToStaticMap.get(method);
   }
 
-  public JArrayType getTypeArray(JType leafType, int dimensions) {
-    assert (!(leafType instanceof JArrayType));
-    HashMap<JType, JArrayType> typeToArrayType;
-
-    // Create typeToArrayType maps for index slots that don't exist yet.
-    //
-    for (int i = this.dimensions.size(); i < dimensions; ++i) {
-      typeToArrayType = new HashMap<JType, JArrayType>();
-      this.dimensions.add(typeToArrayType);
-    }
-
-    // Get the map for array having this number of dimensions (biased by one
-    // since we don't store non-arrays in there -- thus index 0 => 1 dim).
-    //
-    typeToArrayType = this.dimensions.get(dimensions - 1);
-
-    JArrayType arrayType = typeToArrayType.get(leafType);
+  public JArrayType getTypeArray(JType elementType) {
+    JArrayType arrayType = arrayTypes.get(elementType);
     if (arrayType == null) {
-      JType elementType;
-      if (dimensions == 1) {
-        elementType = leafType;
-      } else {
-        elementType = getTypeArray(leafType, dimensions - 1);
-      }
-      arrayType = new JArrayType(elementType, leafType, dimensions);
-      allArrayTypes.add(arrayType);
-
-      /*
-       * TODO(later): should we setup the various array types as an inheritance
-       * heirarchy? Currently we're just doing all the heavy lifting in
-       * JTypeOracle. If we tried to setup inheritance, we'd have to recompute
-       * JTypeOracle if anything changed, so maybe this is better.
-       */
-      typeToArrayType.put(leafType, arrayType);
+      arrayType = new JArrayType(elementType);
+      arrayTypes.put(elementType, arrayType);
     }
-
     return arrayType;
   }
 
+  public JArrayType getTypeArray(JType leafType, int dimensions) {
+    assert dimensions > 0;
+    assert (!(leafType instanceof JArrayType));
+    JArrayType result = getTypeArray(leafType);
+    while (dimensions > 1) {
+      result = getTypeArray(result);
+      --dimensions;
+    }
+    return result;
+  }
+
   public JClassType getTypeClassLiteralHolder() {
     return typeSpecialClassLiteralHolder;
   }
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 8e4aeb3..09291fb 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
@@ -881,7 +881,7 @@
    */
   private boolean isInstantiatedType(JReferenceType type,
       Set<JReferenceType> instantiatedTypes) {
-    type = program.getRunTimeType(type);
+    type = type.getUnderlyingType();
 
     if (type.isExternal()) {
       // TODO(tobyr) I don't know under what situations it is safe to assume
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 7504ea6..e023e72 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
@@ -190,7 +190,7 @@
       JType elementType = type.getElementType();
       int leafQueryId = -1;
       if (elementType instanceof JReferenceType) {
-        leafQueryId = program.getQueryId(program.getRunTimeType((JReferenceType) elementType));
+        leafQueryId = program.getQueryId(((JReferenceType) elementType).getUnderlyingType());
       }
       return leafQueryId;
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
index c0ee478..9cbbf1a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
@@ -199,7 +199,7 @@
       if (type == null || alreadyRan.contains(type)) {
         return;
       }
-      assert (type == program.getRunTimeType(type));
+      assert (type == type.getUnderlyingType());
 
       alreadyRan.add(type);
 
@@ -274,9 +274,9 @@
 
     private void recordCast(JType targetType, JExpression rhs) {
       if (targetType instanceof JReferenceType) {
-        targetType = program.getRunTimeType((JReferenceType) targetType);
+        targetType = ((JReferenceType) targetType).getUnderlyingType();
         // unconditional cast b/c it would've been a semantic error earlier
-        JReferenceType rhsType = program.getRunTimeType((JReferenceType) rhs.getType());
+        JReferenceType rhsType = ((JReferenceType) rhs.getType()).getUnderlyingType();
         // don't record a type for trivial casts that won't generate code
         if (program.typeOracle.canTriviallyCast(rhsType,
             (JReferenceType) targetType)) {
@@ -294,8 +294,8 @@
 
     private void recordCastInternal(JReferenceType toType,
         JReferenceType rhsType) {
-      toType = program.getRunTimeType(toType);
-      rhsType = program.getRunTimeType(rhsType);
+      toType = toType.getUnderlyingType();
+      rhsType = rhsType.getUnderlyingType();
       Set<JReferenceType> querySet = queriedTypes.get(toType);
       if (querySet == null) {
         queryIds.put(toType, nextQueryId++);
@@ -420,7 +420,7 @@
         replaceExpr = call;
       } else if (toType instanceof JReferenceType) {
         JExpression curExpr = expr;
-        JReferenceType refType = program.getRunTimeType((JReferenceType) toType);
+        JReferenceType refType = ((JReferenceType) toType).getUnderlyingType();
         JReferenceType argType = (JReferenceType) expr.getType();
         if (program.typeOracle.canTriviallyCast(argType, refType)) {
           // just remove the cast
@@ -536,7 +536,7 @@
       JReferenceType argType = (JReferenceType) x.getExpr().getType();
       JReferenceType toType = x.getTestType();
       // Only tests on run-time types are supported
-      assert (toType == program.getRunTimeType(toType));
+      assert (toType == toType.getUnderlyingType());
       if (program.typeOracle.canTriviallyCast(argType, toType)) {
         // trivially true if non-null; replace with a null test
         JNullLiteral nullLit = program.getLiteralNull();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
index ef4c02e..de107d4 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
@@ -526,7 +526,7 @@
      * initializer, of new int[0].
      */
     constructorCall.setArg(1, JNewArray.createInitializers(program, info,
-        program.getTypeArray(JPrimitiveType.INT, 1), intExprs));
+        program.getTypeArray(JPrimitiveType.INT), intExprs));
   }
 
   private static <T> T last(T[] array) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
index b67bb11..64c0a9b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -555,7 +555,7 @@
        * Track references and instantiability at the granularity of run-time
        * types. For example, ignore nullness.
        */
-      type = program.getRunTimeType(type);
+      type = type.getUnderlyingType();
 
       boolean doVisit = false;
       if (isInstantiated && !instantiatedTypes.contains(type)) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index 46c1653..a346f4c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -2904,7 +2904,7 @@
       {
         // $VALUES = new E[]{A,B,B};
         SourceInfo fieldInfo = type.getSourceInfo().makeChild();
-        JArrayType enumArrayType = program.getTypeArray(type, 1);
+        JArrayType enumArrayType = program.getTypeArray(type);
         valuesField = program.createField(fieldInfo, "$VALUES", type,
             enumArrayType, true, Disposition.FINAL);
         fieldInfo.addCorrelation(program.getCorrelator().by(valuesField));
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 aec9b82..82531e9 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
@@ -144,7 +144,6 @@
 import com.google.gwt.dev.util.collect.IdentityHashSet;
 import com.google.gwt.dev.util.collect.Maps;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumMap;
@@ -262,7 +261,7 @@
        * Make sure we record all of the program's array types since
        * JProgram.traverse() doesn't iterate over them.
        */
-      accept(new ArrayList<JArrayType>(program.getAllArrayTypes()));
+      accept(program.getAllArrayTypes());
     }
 
     @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
index 08bc124..1bf3ea5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
@@ -213,10 +213,8 @@
 
   @Override
   public boolean visit(JArrayType x, Context ctx) {
-    accept(x.getLeafType());
-    for (int i = 0, c = x.getDims(); i < c; ++i) {
-      print("[]");
-    }
+    accept(x.getElementType());
+    print("[]");
     return false;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeMap.java
index 667bc7d..784e2ff 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeMap.java
@@ -16,7 +16,6 @@
 package com.google.gwt.dev.jjs.impl;
 
 import com.google.gwt.dev.jjs.InternalCompilerException;
-import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JField;
@@ -221,20 +220,11 @@
       }
     } else if (binding instanceof ArrayBinding) {
       ArrayBinding arrayBinding = (ArrayBinding) binding;
-
-      // Compute the JType for the leaf type
-      JType leafType = (JType) get(arrayBinding.leafComponentType, failOnNull);
-
-      if (leafType == null) {
+      JType elementType = (JType) get(arrayBinding.elementsType(), failOnNull);
+      if (elementType == null) {
         return null;
       }
-      
-      // Don't create a new JArrayType; use TypeMap to get the singleton
-      // instance
-      JArrayType arrayType = program.getTypeArray(leafType,
-          arrayBinding.dimensions);
-
-      return arrayType;
+      return program.getTypeArray(elementType);
     } else if (binding instanceof BinaryTypeBinding) {
       BinaryTypeBinding binaryBinding = (BinaryTypeBinding) binding;
       String name = BuildTypeMap.dotify(binaryBinding.compoundName);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index a83b59a..3be1164 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -693,7 +693,7 @@
       if (elementType instanceof JReferenceType) {
         JReferenceType refType = (JReferenceType) elementType;
         if (!program.typeOracle.isInstantiatedType(refType)) {
-          return program.getTypeArray(JNullType.INSTANCE, 1);
+          return program.getTypeArray(JNullType.INSTANCE);
         } else if (elementType instanceof JArrayType) {
           JArrayType newElementType = nullifyArrayType((JArrayType) elementType);
           return program.getTypeArray(newElementType.getLeafType(),
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 6d59a36..03d7460 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
@@ -270,12 +270,12 @@
     classBnn = classB.getNonNull();
     classBaseNn = classBase.getNonNull();
 
-    arrayOfA = program.getTypeArray(classA, 1);
-    arrayOfB = program.getTypeArray(classB, 1);
-    arrayOfBSub = program.getTypeArray(classBSub, 1);
-    arrayOfC = program.getTypeArray(classC, 1);
-    arrayOfObject = program.getTypeArray(classObject, 1);
-    arrayOfInt = program.getTypeArray(program.getTypePrimitiveInt(), 1);
+    arrayOfA = program.getTypeArray(classA);
+    arrayOfB = program.getTypeArray(classB);
+    arrayOfBSub = program.getTypeArray(classBSub);
+    arrayOfC = program.getTypeArray(classC);
+    arrayOfObject = program.getTypeArray(classObject);
+    arrayOfInt = program.getTypeArray(program.getTypePrimitiveInt());
     arrayOfArrayOfInt = program.getTypeArray(program.getTypePrimitiveInt(), 2);
 
     arrayOfArrayOfB = program.getTypeArray(classB, 2);