Revision: 8060
Author: [email protected]
Date: Thu May  6 10:32:42 2010
Log: Lazy initial enum maps, preinitialize values array.

Small tweak to avoid initializing the values map until needed. The functionality is rarely used, so eager initialization seemed kind of silly. I suppose a similar argument could be made for the values array.

http://gwt-code-reviews.appspot.com/280801/show
Suggested by: tobyr
Review by: spoon

http://code.google.com/p/google-web-toolkit/source/detail?r=8060

Modified:
 /branches/2.1/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
 /branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
 /branches/2.1/user/super/com/google/gwt/emul/java/lang/Class.java
 /branches/2.1/user/super/com/google/gwt/emul/java/lang/Enum.java
 /branches/2.1/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java

=======================================
--- /branches/2.1/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java Fri Apr 2 14:35:01 2010 +++ /branches/2.1/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java Thu May 6 10:32:42 2010
@@ -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;
=======================================
--- /branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Fri Apr 2 11:54:38 2010 +++ /branches/2.1/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java Thu May 6 10:32:42 2010
@@ -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;
@@ -2090,28 +2094,6 @@
         JLocal local, JExpression value) {
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
@@ -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);
-      }
-      JNewArray newExpr = JNewArray.createInitializers(program, sourceInfo,
-          program.getTypeArray(type, 1), initializers);
-      currentMethodBody.getBlock().addStmt(
-          new JReturnStatement(sourceInfo, newExpr));
+    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);
+      }
+      {
+        // return $VALUES;
+        SourceInfo sourceInfo = currentMethod.getSourceInfo();
+ JFieldRef valuesRef = new JFieldRef(sourceInfo, null, valuesField, type);
+        currentMethodBody.getBlock().addStmt(
+            new JReturnStatement(sourceInfo, valuesRef));
+      }
+      return valuesField;
     }
   }

=======================================
--- /branches/2.1/user/super/com/google/gwt/emul/java/lang/Class.java Tue Jun 2 08:28:10 2009 +++ /branches/2.1/user/super/com/google/gwt/emul/java/lang/Class.java Thu May 6 10:32:42 2010
@@ -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;
   }

@@ -123,6 +124,8 @@
           + (seedName != null ? seedName : "" + clazz.hashCode());
     }
   }
+
+  JavaScriptObject enumValueOfFunc;

   int modifiers;

@@ -133,10 +136,10 @@

   private Class<? super T> enumSuperclass;

-  private String typeName;
-
   private Class<? super T> superclass;

+  private String typeName;
+
   /**
    * Not publicly instantiable.
    *
@@ -159,13 +162,6 @@
     return [email protected]::enumConstantsFunc
         && ([email protected]::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;
+  }
+}
=======================================
--- /branches/2.1/user/super/com/google/gwt/emul/java/lang/Enum.java Wed Feb 18 17:08:17 2009 +++ /branches/2.1/user/super/com/google/gwt/emul/java/lang/Enum.java Thu May 6 10:32:42 2010
@@ -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.");
-    }
-
-    // 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 + ".");
+    JavaScriptObject enumValueOfFunc = enumType.enumValueOfFunc;
+    if (enumValueOfFunc == null) {
+      throw new IllegalArgumentException();
+    }
+    return invokeValueOf(enumValueOfFunc, 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;
   }

   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);
-    }
-    return result;
+    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 valueOf0(JavaScriptObject map,
+  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;
=======================================
--- /branches/2.1/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java Fri Apr 4 09:22:37 2008 +++ /branches/2.1/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java Thu May 6 10:32:42 2010
@@ -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) {
     }

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to