Finishes out basic enum support in the compiler. Some of the Class object based stuff needed for EnumSet/EnumMap to work correctly is not yet implemented.
Review by: mmendez
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1483 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonObject.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonObject.java
index a776064..8602bc0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonObject.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonObject.java
@@ -16,6 +16,7 @@
package com.google.gwt.dev.jjs.ast.js;
import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JProgram;
@@ -54,19 +55,20 @@
}
}
- public final List/* <JsonPropInit> */propInits = new ArrayList/* <JsonPropInit> */();
+ public final List<JsonPropInit> propInits = new ArrayList<JsonPropInit>();
public JsonObject(JProgram program) {
super(program, null);
}
public JType getType() {
- return program.getTypeVoid();
+ // If JavaScriptObject type is not available, just return the Object type
+ JClassType jsoType = program.getJavaScriptObject();
+ return (jsoType != null) ? jsoType : program.getTypeJavaLangObject();
}
public boolean hasSideEffects() {
- for (int i = 0, c = propInits.size(); i < c; ++i) {
- JsonPropInit propInit = ((JsonPropInit) propInits.get(i));
+ for (JsonPropInit propInit : propInits) {
if (propInit.labelExpr.hasSideEffects()
|| propInit.valueExpr.hasSideEffects()) {
return true;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
index 51258e9..93873ad 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -27,7 +27,6 @@
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
-import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.js.JsParser;
@@ -41,9 +40,11 @@
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
@@ -181,6 +182,14 @@
false);
mapThrownExceptions(newMethod, b);
+ // Enums have hidden arguments for name and value
+ if (enclosingType instanceof JEnumType) {
+ program.createParameter(info, "enum$name".toCharArray(),
+ program.getTypeJavaLangString(), true, newMethod);
+ program.createParameter(info, "enum$ordinal".toCharArray(),
+ program.getTypePrimitiveInt(), true, newMethod);
+ }
+
// user args
mapParameters(newMethod, ctorDecl);
// original params are now frozen
@@ -231,8 +240,14 @@
FieldBinding b = fieldDeclaration.binding;
SourceInfo info = makeSourceInfo(fieldDeclaration);
JReferenceType enclosingType = (JReferenceType) typeMap.get(scope.enclosingSourceType());
- createField(info, b, enclosingType,
- fieldDeclaration.initialization != null);
+ Expression initialization = fieldDeclaration.initialization;
+ if (initialization != null
+ && initialization instanceof AllocationExpression
+ && ((AllocationExpression) initialization).enumConstant != null) {
+ createEnumField(info, b, enclosingType);
+ } else {
+ createField(info, b, enclosingType, initialization != null);
+ }
return true;
} catch (Throwable e) {
throw translateException(fieldDeclaration, e);
@@ -290,17 +305,20 @@
return process(typeDeclaration);
}
+ private JField createEnumField(SourceInfo info, FieldBinding binding,
+ JReferenceType enclosingType) {
+ JType type = (JType) typeMap.get(binding.type);
+ JField field = program.createEnumField(info, binding.name,
+ (JEnumType) enclosingType, (JClassType) type, binding.original().id);
+ typeMap.put(binding, field);
+ return field;
+ }
+
private JField createField(SourceInfo info, FieldBinding binding,
JReferenceType enclosingType, boolean hasInitializer) {
JType type = (JType) typeMap.get(binding.type);
- JField field;
- if (binding.declaringClass.isEnum()) {
- field = program.createEnumField(info, binding.name,
- (JEnumType) enclosingType, (JClassType) type, binding.original().id);
- } else {
- field = program.createField(info, binding.name, enclosingType, type,
- binding.isStatic(), binding.isFinal(), hasInitializer);
- }
+ JField field = program.createField(info, binding.name, enclosingType,
+ type, binding.isStatic(), binding.isFinal(), hasInitializer);
typeMap.put(binding, field);
return field;
}
@@ -400,7 +418,7 @@
currentFileName = String.valueOf(compResult.fileName);
SourceTypeBinding binding = typeDeclaration.binding;
if (binding.isAnnotationType()) {
- // TODO
+ // Ignore these.
return false;
}
if (binding.constantPoolName() == null) {
@@ -448,10 +466,12 @@
JInterfaceType superInterface = (JInterfaceType) typeMap.get(superInterfaceBinding);
type.implments.add(superInterface);
}
- typeDecls.add(typeDeclaration);
+
if (binding.isEnum()) {
- processEnum(binding, type);
+ processEnumType(binding, (JEnumType) type);
}
+
+ typeDecls.add(typeDeclaration);
return true;
} catch (InternalCompilerException ice) {
ice.addNode(type);
@@ -461,7 +481,7 @@
}
}
- private void processEnum(SourceTypeBinding binding, JReferenceType type) {
+ private void processEnumType(SourceTypeBinding binding, JEnumType type) {
// Visit the synthetic values() and valueOf() methods.
for (MethodBinding methodBinding : binding.methods) {
if (methodBinding instanceof SyntheticMethodBinding) {
@@ -469,22 +489,15 @@
TypeBinding[] parameters = methodBinding.parameters;
if (parameters.length == 0) {
assert newMethod.getName().equals("values");
- // TODO: hack
- JMethodBody body = (JMethodBody) newMethod.getBody();
- body.getStatements().add(
- new JReturnStatement(program, null, program.getLiteralNull()));
} else if (parameters.length == 1) {
assert newMethod.getName().equals("valueOf");
assert typeMap.get(parameters[0]) == program.getTypeJavaLangString();
program.createParameter(null, "name".toCharArray(),
program.getTypeJavaLangString(), true, newMethod);
- // TODO: hack
- JMethodBody body = (JMethodBody) newMethod.getBody();
- body.getStatements().add(
- new JReturnStatement(program, null, program.getLiteralNull()));
} else {
assert false;
}
+ newMethod.freezeParamTypes();
}
}
}
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 0119aff..e80bdac 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
@@ -37,6 +37,7 @@
import com.google.gwt.dev.jjs.ast.JDoStatement;
import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
import com.google.gwt.dev.jjs.ast.JEnumField;
+import com.google.gwt.dev.jjs.ast.JEnumType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JExpressionStatement;
import com.google.gwt.dev.jjs.ast.JField;
@@ -80,6 +81,7 @@
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.JsFunction;
@@ -260,14 +262,42 @@
program = this.typeMap.getProgram();
}
+ public void processEnumType(JEnumType type) {
+ // Create a JSNI map for string-based lookup.
+ JField mapField = createEnumValueMap(type);
+
+ // Generate the synthetic values() and valueOf() methods.
+ for (JMethod method : type.methods) {
+ currentMethod = method;
+ if ("values".equals(method.getName())) {
+ if (method.params.size() != 0) {
+ continue;
+ }
+ currentMethodBody = (JMethodBody) method.getBody();
+ writeEnumValuesMethod(type);
+ } else if ("valueOf".equals(method.getName())) {
+ if (method.params.size() != 1) {
+ continue;
+ }
+ if (method.params.get(0).getType() != program.getTypeJavaLangString()) {
+ continue;
+ }
+ currentMethodBody = (JMethodBody) method.getBody();
+ writeEnumValueOfMethod(type, mapField);
+ }
+ currentMethodBody = null;
+ currentMethod = null;
+ }
+ }
+
/**
* We emulate static initializers and instance initializers as methods. As
* in other cases, this gives us: simpler AST, easier to optimize, more like
* output JavaScript.
*/
public void processType(TypeDeclaration x) {
- if (x.binding.isEnum() || x.binding.isAnnotationType()) {
- // TODO
+ if (x.binding.isAnnotationType()) {
+ // Do not process.
return;
}
currentClass = (JReferenceType) typeMap.get(x.binding);
@@ -331,6 +361,10 @@
}
}
+ if (x.binding.isEnum()) {
+ processEnumType((JEnumType) currentClass);
+ }
+
currentClassScope = null;
currentClass = null;
currentSeparatorPositions = null;
@@ -569,6 +603,17 @@
}
}
+ // Enums: wire up synthetic name/ordinal params to the super method.
+ if (enclosingType instanceof JEnumType) {
+ assert (superOrThisCall != null);
+ JVariableRef enumNameRef = createVariableRef(
+ superOrThisCall.getSourceInfo(), ctor.params.get(0));
+ superOrThisCall.getArgs().add(0, enumNameRef);
+ JVariableRef enumOrdinalRef = createVariableRef(
+ superOrThisCall.getSourceInfo(), ctor.params.get(1));
+ superOrThisCall.getArgs().add(1, enumOrdinalRef);
+ }
+
// optional this or super constructor call
if (superOrThisCall != null) {
statements.add(superOrThisCall.makeStatement());
@@ -658,6 +703,13 @@
call = new JMethodCall(program, info, newInstance, ctor);
}
+ // Enums: hidden arguments for the name and id.
+ if (x.enumConstant != null) {
+ call.getArgs().add(program.getLiteralString(x.enumConstant.name));
+ call.getArgs().add(
+ program.getLiteralInt(x.enumConstant.binding.original().id));
+ }
+
// Plain old regular user arguments
if (x.arguments != null) {
for (int i = 0, n = x.arguments.length; i < n; ++i) {
@@ -1273,6 +1325,11 @@
initializer = dispProcessExpression(declaration.initialization);
}
+ if (field instanceof JEnumField) {
+ // An enum field must be initialized!
+ assert (initializer instanceof JMethodCall);
+ }
+
if (initializer instanceof JLiteral) {
field.constInitializer = (JLiteral) initializer;
} else if (initializer != null) {
@@ -1768,7 +1825,7 @@
String varName = String.valueOf(arg.name);
JParameter param = null;
for (int i = 0; i < currentMethod.params.size(); ++i) {
- JParameter paramIt = (JParameter) currentMethod.params.get(i);
+ JParameter paramIt = currentMethod.params.get(i);
if (varType == paramIt.getType()
&& varName.equals(paramIt.getName())) {
param = paramIt;
@@ -1879,6 +1936,25 @@
return call;
}
+ private JField createEnumValueMap(JEnumType type) {
+ JsonObject map = new JsonObject(program);
+ for (JEnumField field : type.enumList) {
+ // JSON maps require leading underscores to prevent collisions.
+ JStringLiteral key = program.getLiteralString("_" + field.getName());
+ JFieldRef value = new JFieldRef(program, null, null, field, type);
+ map.propInits.add(new JsonObject.JsonPropInit(program, key, value));
+ }
+ JField mapField = program.createField(null, "enum$map".toCharArray(),
+ type, map.getType(), true, true, true);
+
+ // Initialize in clinit.
+ JMethodBody clinitBody = (JMethodBody) type.methods.get(0).getBody();
+ JExpressionStatement assignment = program.createAssignmentStmt(null,
+ createVariableRef(null, mapField), map);
+ clinitBody.getStatements().add(assignment);
+ return mapField;
+ }
+
private JLocalDeclarationStatement createLocalDeclaration(SourceInfo info,
JLocal arrayVar, JExpression value) {
return new JLocalDeclarationStatement(program, info, new JLocalRef(
@@ -2309,6 +2385,32 @@
toUnbox, valueMethod);
return unboxCall;
}
+
+ private void writeEnumValueOfMethod(JEnumType type, JField mapField) {
+ // return Enum.valueOf(map, name);
+ JFieldRef mapRef = new JFieldRef(program, null, null, mapField, type);
+ JVariableRef nameRef = createVariableRef(null,
+ currentMethod.params.get(0));
+ JMethod delegateTo = program.getIndexedMethod("Enum.valueOf");
+ JMethodCall call = new JMethodCall(program, null, null, delegateTo);
+ call.getArgs().add(mapRef);
+ call.getArgs().add(nameRef);
+ currentMethodBody.getStatements().add(
+ new JReturnStatement(program, null, call));
+ }
+
+ private void writeEnumValuesMethod(JEnumType type) {
+ // return new E[]{A,B,C};
+ JNewArray newExpr = new JNewArray(program, null, program.getTypeArray(
+ type, 1));
+ newExpr.initializers = new ArrayList<JExpression>();
+ for (JEnumField field : type.enumList) {
+ JFieldRef fieldRef = new JFieldRef(program, null, null, field, type);
+ newExpr.initializers.add(fieldRef);
+ }
+ currentMethodBody.getStatements().add(
+ new JReturnStatement(program, null, newExpr));
+ }
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index aa37346..ad688a5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -23,7 +23,6 @@
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
-import com.google.gwt.dev.jjs.ast.JEnumField;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JFieldRef;
@@ -375,10 +374,6 @@
if (target.isStatic()) {
rescue(target.getEnclosingType(), true, false);
}
- // TODO: HACK
- if (target instanceof JEnumField) {
- rescue(((JEnumField) target).getEnclosingType(), true, true);
- }
rescue(target);
return true;
}
@@ -570,6 +565,9 @@
/*
* Any reference types (except String, which works by default) that take
* part in a concat must rescue java.lang.Object.toString().
+ *
+ * TODO: can we narrow the focus by walking up the type heirarchy or
+ * doing explicit toString calls?
*/
JMethod toStringMethod = program.getIndexedMethod("Object.toString");
rescue(toStringMethod);
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 f11649c..151b4b0 100644
--- a/user/super/com/google/gwt/emul/java/lang/Enum.java
+++ b/user/super/com/google/gwt/emul/java/lang/Enum.java
@@ -15,6 +15,8 @@
*/
package java.lang;
+import com.google.gwt.core.client.JavaScriptObject;
+
/**
* The first-class representation of an enumeration.
*
@@ -22,6 +24,20 @@
*/
public abstract class Enum<E extends Enum<E>> implements Comparable<E> {
+ 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;
+ }
+
+ private static native <T extends Enum<T>> T valueOf0(JavaScriptObject map,
+ String name) /*-{
+ return map[name] || null;
+ }-*/;
+
// public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
// {
// throw new UnsupportedOperationException("not yet implemented.");
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 34dfcb9..460ee10 100644
--- a/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/EnumsTest.java
@@ -168,28 +168,46 @@
assertEquals(Basic.A, Basic.valueOf("A"));
assertEquals(Basic.B, Basic.valueOf("B"));
assertEquals(Basic.C, Basic.valueOf("C"));
+ try {
+ Basic.valueOf("D");
+ fail("Basic.valueOf(\"D\") -- expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ }
assertEquals(Complex.A, Complex.valueOf("A"));
assertEquals(Complex.B, Complex.valueOf("B"));
assertEquals(Complex.C, Complex.valueOf("C"));
+ try {
+ Complex.valueOf("D");
+ fail("Complex.valueOf(\"D\") -- expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ }
assertEquals(Subclassing.A, Subclassing.valueOf("A"));
assertEquals(Subclassing.B, Subclassing.valueOf("B"));
assertEquals(Subclassing.C, Subclassing.valueOf("C"));
+ try {
+ Subclassing.valueOf("D");
+ fail("Subclassing.valueOf(\"D\") -- expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ }
}
public void testValues() {
Basic[] simples = Basic.values();
+ assertEquals(3, simples.length);
assertEquals(Basic.A, simples[0]);
assertEquals(Basic.B, simples[1]);
assertEquals(Basic.C, simples[2]);
Complex[] complexes = Complex.values();
+ assertEquals(3, complexes.length);
assertEquals(Complex.A, complexes[0]);
assertEquals(Complex.B, complexes[1]);
assertEquals(Complex.C, complexes[2]);
Subclassing[] subs = Subclassing.values();
+ assertEquals(3, subs.length);
assertEquals(Subclassing.A, subs[0]);
assertEquals(Subclassing.B, subs[1]);
assertEquals(Subclassing.C, subs[2]);