Merges r8060 from the 2.1 I/O branch into trunk.
This lazy initializes enum maps.

Review by: mmendez@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8224 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
index ab59688..92ba61c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
@@ -94,25 +94,37 @@
       if (classType instanceof JEnumType) {
         JEnumType enumType = (JEnumType) classType;
         JMethod valuesMethod = null;
+        JMethod valueOfMethod = null;
         for (JMethod methodIt : enumType.getMethods()) {
           if ("values".equals(methodIt.getName())) {
             if (methodIt.getParams().size() != 0) {
               continue;
             }
             valuesMethod = methodIt;
-            break;
+          }
+          if ("valueOf".equals(methodIt.getName())) {
+            if (methodIt.getParams().size() != 1) {
+              continue;
+            }
+            valueOfMethod = methodIt;
           }
         }
         if (valuesMethod == null) {
           throw new InternalCompilerException(
               "Could not find enum values() method");
         }
-        JsniMethodRef jsniMethodRef = new JsniMethodRef(info, null,
-            valuesMethod, program.getJavaScriptObject());
-        call.addArg(jsniMethodRef);
+        if (valueOfMethod == null) {
+          throw new InternalCompilerException(
+              "Could not find enum valueOf() method");
+        }
+        call.addArg(new JsniMethodRef(info, null, valuesMethod,
+            program.getJavaScriptObject()));
+        call.addArg(new JsniMethodRef(info, null, valueOfMethod,
+            program.getJavaScriptObject()));
       } else if (isEnumOrSubclass) {
         // A subclass of an enum class
         call.addArg(program.getLiteralNull());
+        call.addArg(program.getLiteralNull());
       }
     } else if (type instanceof JArrayType) {
       JArrayType arrayType = (JArrayType) 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 564b9ed..0969d62 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
@@ -95,7 +95,6 @@
 import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
-import com.google.gwt.dev.jjs.ast.js.JsonObject;
 import com.google.gwt.dev.js.ast.JsContext;
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsModVisitor;
@@ -342,10 +341,8 @@
     }
 
     public void processEnumType(JEnumType type) {
-      // Create a JSNI map for string-based lookup.
-      JField mapField = createEnumValueMap(type);
-
       // Generate the synthetic values() and valueOf() methods.
+      JField valuesField = null;
       for (JMethod method : type.getMethods()) {
         currentMethod = method;
         if ("values".equals(method.getName())) {
@@ -353,8 +350,15 @@
             continue;
           }
           currentMethodBody = (JMethodBody) method.getBody();
-          writeEnumValuesMethod(type);
-        } else if ("valueOf".equals(method.getName())) {
+          valuesField = writeEnumValuesMethod(type);
+        }
+        currentMethodBody = null;
+        currentMethod = null;
+      }
+      // Generate the synthetic values() and valueOf() methods.
+      for (JMethod method : type.getMethods()) {
+        currentMethod = method;
+        if ("valueOf".equals(method.getName())) {
           if (method.getParams().size() != 1) {
             continue;
           }
@@ -362,7 +366,7 @@
             continue;
           }
           currentMethodBody = (JMethodBody) method.getBody();
-          writeEnumValueOfMethod(type, mapField);
+          writeEnumValueOfMethod(type, valuesField);
         }
         currentMethodBody = null;
         currentMethod = null;
@@ -2091,28 +2095,6 @@
       return new JDeclarationStatement(info, new JLocalRef(info, local), value);
     }
 
-    private JField createEnumValueMap(JEnumType type) {
-      SourceInfo sourceInfo = type.getSourceInfo().makeChild(
-          JavaASTGenerationVisitor.class, "enum value lookup map");
-      JsonObject map = new JsonObject(sourceInfo, program.getJavaScriptObject());
-      for (JEnumField field : type.getEnumList()) {
-        // JSON maps require leading underscores to prevent collisions.
-        JStringLiteral key = program.getLiteralString(field.getSourceInfo(),
-            "_" + field.getName());
-        JFieldRef value = new JFieldRef(sourceInfo, null, field, type);
-        map.propInits.add(new JsonObject.JsonPropInit(sourceInfo, key, value));
-      }
-      JField mapField = program.createField(sourceInfo, "enum$map", type,
-          map.getType(), true, Disposition.FINAL);
-
-      // Initialize in clinit.
-      JMethodBody clinitBody = (JMethodBody) type.getMethods().get(0).getBody();
-      JExpressionStatement assignment = JProgram.createAssignmentStmt(
-          sourceInfo, createVariableRef(sourceInfo, mapField), map);
-      clinitBody.getBlock().addStmt(assignment);
-      return mapField;
-    }
-
     /**
      * Helper to create a qualified "this" ref (really a synthetic this field
      * access) of the appropriate type. Always use this method instead of
@@ -2755,33 +2737,84 @@
       return unboxCall;
     }
 
-    private void writeEnumValueOfMethod(JEnumType type, JField mapField) {
-      // return Enum.valueOf(map, name);
-      SourceInfo sourceInfo = mapField.getSourceInfo().makeChild(
-          JavaASTGenerationVisitor.class, "enum accessor method");
-      JFieldRef mapRef = new JFieldRef(sourceInfo, null, mapField, type);
-      JVariableRef nameRef = createVariableRef(sourceInfo,
-          currentMethod.getParams().get(0));
-      JMethod delegateTo = program.getIndexedMethod("Enum.valueOf");
-      JMethodCall call = new JMethodCall(sourceInfo, null, delegateTo);
-      call.addArgs(mapRef, nameRef);
-      currentMethodBody.getBlock().addStmt(
-          new JReturnStatement(sourceInfo, call));
+    private void writeEnumValueOfMethod(JEnumType type, JField valuesField) {
+      JField mapField;
+      {
+        /*
+         * Make an inner class to hold a lazy-init name-value map. We use a
+         * class to take advantage of its clinit.
+         * 
+         * class Map { $MAP = Enum.createValueOfMap($VALUES); }
+         */
+        SourceInfo sourceInfo = type.getSourceInfo().makeChild(
+            JavaASTGenerationVisitor.class, "Enum$Map");
+        JClassType mapClass = program.createClass(sourceInfo, type.getName()
+            + "$Map", false, true);
+        mapField = program.createField(sourceInfo, "$MAP", mapClass,
+            program.getJavaScriptObject(), true, Disposition.FINAL);
+
+        JMethodCall call = new JMethodCall(sourceInfo, null,
+            program.getIndexedMethod("Enum.createValueOfMap"));
+        call.addArg(new JFieldRef(sourceInfo, null, valuesField, type));
+        JFieldRef mapRef = new JFieldRef(sourceInfo, null, mapField, type);
+        JDeclarationStatement declStmt = new JDeclarationStatement(sourceInfo,
+            mapRef, call);
+        JMethod clinit = program.createMethod(sourceInfo, "$clinit", mapClass,
+            program.getTypeVoid(), false, true, true, true, false);
+        clinit.freezeParamTypes();
+        JBlock clinitBlock = ((JMethodBody) clinit.getBody()).getBlock();
+        clinitBlock.addStmt(declStmt);
+        mapField.setInitializer(declStmt);
+      }
+
+      /*
+       * return Enum.valueOf(Enum$Map.Map.$MAP, name);
+       */
+      {
+        SourceInfo sourceInfo = currentMethodBody.getSourceInfo();
+        JFieldRef mapRef = new JFieldRef(sourceInfo, null, mapField, type);
+        JVariableRef nameRef = createVariableRef(sourceInfo,
+            currentMethod.getParams().get(0));
+        JMethod delegateTo = program.getIndexedMethod("Enum.valueOf");
+        JMethodCall call = new JMethodCall(sourceInfo, null, delegateTo);
+        call.addArgs(mapRef, nameRef);
+
+        currentMethodBody.getBlock().addStmt(
+            new JReturnStatement(sourceInfo, call));
+      }
     }
 
-    private void writeEnumValuesMethod(JEnumType type) {
-      // return new E[]{A,B,C};
-      SourceInfo sourceInfo = type.getSourceInfo().makeChild(
-          JavaASTGenerationVisitor.class, "enum values method");
-      List<JExpression> initializers = new ArrayList<JExpression>();
-      for (JEnumField field : type.getEnumList()) {
-        JFieldRef fieldRef = new JFieldRef(sourceInfo, null, field, type);
-        initializers.add(fieldRef);
+    private JField writeEnumValuesMethod(JEnumType type) {
+      JField valuesField;
+      {
+        // $VALUES = new E[]{A,B,B};
+        SourceInfo sourceInfo = type.getSourceInfo().makeChild(
+            JavaASTGenerationVisitor.class, "$VALUES");
+        JArrayType enumArrayType = program.getTypeArray(type, 1);
+        valuesField = program.createField(sourceInfo, "$VALUES", type,
+            enumArrayType, true, Disposition.FINAL);
+        List<JExpression> initializers = new ArrayList<JExpression>();
+        for (JEnumField field : type.getEnumList()) {
+          JFieldRef fieldRef = new JFieldRef(sourceInfo, null, field, type);
+          initializers.add(fieldRef);
+        }
+        JNewArray newExpr = JNewArray.createInitializers(program, sourceInfo,
+            enumArrayType, initializers);
+        JFieldRef valuesRef = new JFieldRef(sourceInfo, null, valuesField, type);
+        JDeclarationStatement declStmt = new JDeclarationStatement(sourceInfo,
+            valuesRef, newExpr);
+        JBlock clinitBlock = ((JMethodBody) type.getMethods().get(0).getBody()).getBlock();
+        clinitBlock.addStmt(declStmt);
+        valuesField.setInitializer(declStmt);
       }
-      JNewArray newExpr = JNewArray.createInitializers(program, sourceInfo,
-          program.getTypeArray(type, 1), initializers);
-      currentMethodBody.getBlock().addStmt(
-          new JReturnStatement(sourceInfo, newExpr));
+      {
+        // return $VALUES;
+        SourceInfo sourceInfo = currentMethod.getSourceInfo();
+        JFieldRef valuesRef = new JFieldRef(sourceInfo, null, valuesField, type);
+        currentMethodBody.getBlock().addStmt(
+            new JReturnStatement(sourceInfo, valuesRef));
+      }
+      return valuesField;
     }
   }
 
diff --git a/user/super/com/google/gwt/emul/java/lang/Class.java b/user/super/com/google/gwt/emul/java/lang/Class.java
index 3526d69..d000d2d 100644
--- a/user/super/com/google/gwt/emul/java/lang/Class.java
+++ b/user/super/com/google/gwt/emul/java/lang/Class.java
@@ -67,13 +67,14 @@
    */
   static <T> Class<T> createForEnum(String packageName, String className,
       String seedName, Class<? super T> superclass,
-      JavaScriptObject enumConstantsFunc) {
+      JavaScriptObject enumConstantsFunc, JavaScriptObject enumValueOfFunc) {
     // Initialize here to avoid method inliner
     Class<T> clazz = new Class<T>();
     setName(clazz, packageName, className, seedName);
     clazz.modifiers = (enumConstantsFunc != null) ? ENUM : 0;
     clazz.superclass = clazz.enumSuperclass = superclass;
     clazz.enumConstantsFunc = enumConstantsFunc;
+    clazz.enumValueOfFunc = enumValueOfFunc;
     return clazz;
   }
 
@@ -124,6 +125,8 @@
     }
   }
 
+  JavaScriptObject enumValueOfFunc;
+
   int modifiers;
 
   private Class<?> componentType;
@@ -133,10 +136,10 @@
 
   private Class<? super T> enumSuperclass;
 
-  private String typeName;
-
   private Class<? super T> superclass;
 
+  private String typeName;
+
   /**
    * Not publicly instantiable.
    * 
@@ -160,13 +163,6 @@
         && (this.@java.lang.Class::enumConstantsFunc)();
   }-*/;
 
-  /**
-   * Used by Enum to allow getSuperclass() to be pruned.
-   */
-  public Class<? super T> getEnumSuperclass() {
-    return enumSuperclass;
-  }
-
   public String getName() {
     return typeName;
   }
@@ -199,4 +195,11 @@
     return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
         + getName();
   }
+
+  /**
+   * Used by Enum to allow getSuperclass() to be pruned.
+   */
+  Class<? super T> getEnumSuperclass() {
+    return enumSuperclass;
+  }
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/Enum.java b/user/super/com/google/gwt/emul/java/lang/Enum.java
index 4469adf..54513de 100644
--- a/user/super/com/google/gwt/emul/java/lang/Enum.java
+++ b/user/super/com/google/gwt/emul/java/lang/Enum.java
@@ -28,46 +28,49 @@
     Serializable {
 
   public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
-
-    // Have to explicitly test for null, otherwise we won't get 
-    // NullPointerExceptions in web mode
-    if (enumType == null || name == null) {
-      throw new NullPointerException("enumType and name must not be null.");
+    JavaScriptObject enumValueOfFunc = enumType.enumValueOfFunc;
+    if (enumValueOfFunc == null) {
+      throw new IllegalArgumentException();
     }
-
-    // TODO(scottb) Work some compiler magic to improve this from a linear
-    // search to a map lookup.
-    T[] constants = enumType.getEnumConstants();
-
-    if (constants == null) {
-      throw new IllegalArgumentException(
-          enumType.getName() + " is not an enum.");
-    }
-
-    for (T constant : constants) {
-      if (constant.name().equals(name)) {
-        return constant;
-      }
-    }
-
-    throw new IllegalArgumentException(enumType.getName()
-        + " does not have an enum constant named, " + name + ".");
+    return invokeValueOf(enumValueOfFunc, name);
   }
 
-  protected static <T extends Enum<T>> T valueOf(JavaScriptObject map,
-      String name) {
-    T result = Enum.<T> valueOf0(map, "_" + name);
-    if (result == null) {
-      throw new IllegalArgumentException(name);
+  protected static <T extends Enum<T>> JavaScriptObject createValueOfMap(
+      T[] enumConstants) {
+    JavaScriptObject result = JavaScriptObject.createObject();
+    for (T value : enumConstants) {
+      put0(result, ":" + value.name, value);
     }
     return result;
   }
 
-  private static native <T extends Enum<T>> T valueOf0(JavaScriptObject map,
+  protected static <T extends Enum<T>> T valueOf(JavaScriptObject map,
+      String name) {
+    T result = Enum.<T> get0(map, ":" + name);
+    if (result != null) {
+      return result;
+    }
+    if (name == null) {
+      throw new NullPointerException();
+    }
+    throw new IllegalArgumentException();
+  }
+
+  private static native <T extends Enum<T>> T get0(JavaScriptObject map,
       String name) /*-{
     return map[name];
   }-*/;
 
+  private static native <T extends Enum<T>> T invokeValueOf(
+      JavaScriptObject enumValueOfFunc, String name) /*-{
+    return enumValueOfFunc(name);
+  }-*/;
+
+  private static native <T extends Enum<T>> void put0(JavaScriptObject map,
+      String name, T value) /*-{
+    map[name] = value;
+  }-*/;
+
   private final String name;
 
   private final int ordinal;
diff --git a/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java b/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java
index 086f7bc..13eb765 100644
--- a/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.test;
 
+import com.google.gwt.core.client.JavaScriptException;
 import com.google.gwt.junit.client.GWTTestCase;
 
 /**
@@ -222,6 +223,7 @@
       Enum.valueOf(nullEnumClass, "foo");
       fail("Passed a null enum class to Enum.valueOf; expected "
           + "NullPointerException");
+    } catch (JavaScriptException e) {
     } catch (NullPointerException e) {
     }