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