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) {
}