Two tweaks to improve Array instantiation performance.

1) For reference-type arrays, we no longer need to initialize to 'null' explicitly since 'undefined' works just as well.

2) Instead of caching a protoTypeArray and using for-in to copy expandos, cache the names and values in a pair of arrays.  Array iteration is generally faster than for-in.  This also uses the "static-initializer-inner-class" pattern to avoid lazy checks and also factor the code out.

Review by: bobv

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3651 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 30f3f7e..4e907dc 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
@@ -21,6 +21,55 @@
  */
 public final class Array {
 
+  private static final class ExpandoWrapper {
+    /**
+     * A JS array containing the names of any expandos we need to add to arrays
+     * (such as "hashCode", "equals", "toString").
+     */
+    private static final Object expandoNames = makeEmptyJsArray();
+
+    /**
+     * A JS array containing the values of any expandos we need to add to arrays
+     * (such as hashCode(), equals(), toString()).
+     */
+    private static final Object expandoValues = makeEmptyJsArray();
+
+    static {
+      initExpandos(new Array(), expandoNames, expandoValues);
+    }
+
+    public static void wrapArray(Array array) {
+      wrapArray(array, expandoNames, expandoValues);
+    }
+
+    private static native void initExpandos(Array protoType,
+        Object expandoNames, Object expandoValues) /*-{
+      var i = 0, value;
+      for (var name in protoType) {
+        // Only copy non-null values over; this generally means only functions
+        // will get copied over, and not fields, which is good because we will
+        // setup the fields manually and it's best if length doesn't get blown
+        // away.
+        if (value = protoType[name]) {
+          expandoNames[i] = name;
+          expandoValues[i] = value;
+          ++i;
+        }
+      }
+    }-*/;
+
+    private static native Object makeEmptyJsArray() /*-{
+      return [];
+    }-*/;
+
+    private static native void wrapArray(Array array, Object expandoNames,
+        Object expandoValues) /*-{
+      for (var i = 0, c = expandoNames.length; i < c; ++i) {
+        array[expandoNames[i]] = expandoValues[i];
+      }
+    }-*/;
+  }
+
   /*
    * TODO: static init instead of lazy init when we can elide the clinit calls.
    */
@@ -34,12 +83,6 @@
   static final int ZERO_SEED_TYPE = 1;
 
   /**
-   * Stores the prototype Array so that arrays can get their polymorphic methods
-   * via expando.
-   */
-  private static Array protoTypeArray;
-
-  /**
    * Creates a copy of the specified array.
    */
   public static <T> T[] clone(T[] array) {
@@ -123,10 +166,7 @@
    */
   public static Array initValues(Class arrayClass, int typeId, int queryId,
       Array array) {
-    if (protoTypeArray == null) {
-      protoTypeArray = new Array();
-    }
-    wrapArray(array, protoTypeArray);
+    ExpandoWrapper.wrapArray(array);
     array.arrayClass = arrayClass;
     Util.setTypeId(array, typeId);
     array.queryId = queryId;
@@ -151,7 +191,7 @@
   private static native Array arraySlice(Array array, int fromIndex, int toIndex) /*-{
     return array.slice(fromIndex, toIndex);
   }-*/;
-  
+
   /**
    * Use JSNI to effect a castless type change.
    */
@@ -177,11 +217,12 @@
    * @return the new JSON array
    */
   private static native Array createFromSeed(int seedType, int length) /*-{
-    var seedArray = [null, 0, false, [0, 0]];
-    var value = seedArray[seedType];
     var array = new Array(length);
-    for (var i = 0; i < length; ++i) {
-      array[i] = value;
+    if (seedType > 0) {
+      var value = [null, 0, false, [0, 0]][seedType];
+      for (var i = 0; i < length; ++i) {
+        array[i] = value;
+      }
     }
     return array;
   }-*/;
@@ -212,23 +253,9 @@
     return array[index] = value;
   }-*/;
 
-  private static native Array wrapArray(Array array, Array protoTypeArray) /*-{
-    for (var i in protoTypeArray) {
-      // Only copy non-null values over; this generally means only functions
-      // will get copied over, and not fields, which is fine because we will
-      // setup the fields manually and it's best if length doesn't get blown
-      // away.
-      var toCopy = protoTypeArray[i];
-      if (toCopy) {
-        array[i] = toCopy;
-      }
-    }
-    return array;
-  }-*/;
-
   /*
    * Explicitly initialize all fields to JS false values; see comment in
-   * wrapArray(Array, Array).
+   * ExpandoWrapper.initExpandos().
    */
   public volatile int length = 0;
   protected Class arrayClass = null;