Implemented Class.getEnumConstants() in the compiler and JRE.
Note: this patch does not implement Enum.valueOf(Class<T> enumType, String name) or Enum.getDeclaringClass().
Review by: mmastrac
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1679 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 eb97f67..b76cede 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
@@ -15,6 +15,9 @@
*/
package com.google.gwt.dev.jjs.ast;
+import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
+
/**
* Java class literal expression.
*/
@@ -77,6 +80,26 @@
}
call.getArgs().add(superclassLiteral);
+
+ if (classType instanceof JEnumType) {
+ JEnumType enumType = (JEnumType) classType;
+ JMethod valuesMethod = null;
+ for (JMethod methodIt : enumType.methods) {
+ if ("values".equals(methodIt.getName())) {
+ if (methodIt.params.size() != 0) {
+ continue;
+ }
+ valuesMethod = methodIt;
+ }
+ }
+ if (valuesMethod == null) {
+ throw new InternalCompilerException(
+ "Could not find enum values() method");
+ }
+ JsniMethodRef jsniMethodRef = new JsniMethodRef(program.program, null,
+ valuesMethod);
+ call.getArgs().add(jsniMethodRef);
+ }
} else {
assert (type instanceof JArrayType || type instanceof JInterfaceType || type instanceof JPrimitiveType);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
index b023c9a..0272c16 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassType.java
@@ -41,6 +41,13 @@
return isAbstract;
}
+ public JEnumType isEnumOrSubclass() {
+ if (extnds != null) {
+ return extnds.isEnumOrSubclass();
+ }
+ return null;
+ }
+
public boolean isFinal() {
return isFinal;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JEnumType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JEnumType.java
index ee929e4..bcd5be9 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JEnumType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JEnumType.java
@@ -24,6 +24,9 @@
* Java enum type reference expression.
*/
public class JEnumType extends JClassType {
+ /*
+ * TODO: implement traverse?
+ */
public final List<JEnumField> enumList = new ArrayList<JEnumField>();
@@ -32,10 +35,13 @@
this.extnds = program.getTypeJavaLangEnum();
}
- // TODO: implement traverse?
-
@Override
public String getClassLiteralFactoryMethod() {
return "Class.createForEnum";
}
+
+ @Override
+ public JEnumType isEnumOrSubclass() {
+ return this;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodRef.java
index 6891900..a0fd09b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniMethodRef.java
@@ -17,9 +17,11 @@
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JVisitor;
/**
@@ -33,6 +35,13 @@
method);
}
+ @Override
+ public JType getType() {
+ // If JavaScriptObject type is not available, just return the Object type
+ JClassType jsoType = program.getJavaScriptObject();
+ return (jsoType != null) ? jsoType : program.getTypeJavaLangObject();
+ }
+
public void traverse(JVisitor visitor, Context ctx) {
if (visitor.visit(this, ctx)) {
}
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 744338e..699dac2 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
@@ -186,7 +186,7 @@
mapThrownExceptions(newMethod, b);
// Enums have hidden arguments for name and value
- if (enclosingType instanceof JEnumType) {
+ if (enclosingType.isEnumOrSubclass() != null) {
program.createParameter(info, "enum$name".toCharArray(),
program.getTypeJavaLangString(), true, false, newMethod);
program.createParameter(info, "enum$ordinal".toCharArray(),
@@ -481,7 +481,7 @@
type.implments.add(superInterface);
}
- if (binding.isEnum()) {
+ if (type instanceof JEnumType) {
processEnumType(binding, (JEnumType) type);
}
@@ -731,7 +731,12 @@
} else if (binding.isInterface()) {
newType = program.createInterface(info, name);
} else if (binding.isEnum()) {
- newType = program.createEnum(info, name);
+ if (binding.isAnonymousType()) {
+ // Don't model an enum subclass as a JEnumType.
+ newType = program.createClass(info, name, false, true);
+ } else {
+ newType = program.createEnum(info, name);
+ }
} else if (binding.isAnnotationType()) {
// TODO
return false;
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 d913c6e..1343814 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
@@ -534,7 +534,7 @@
new JReturnStatement(program, null, classLit));
}
- if (x.binding.isEnum()) {
+ if (currentClass instanceof JEnumType) {
processEnumType((JEnumType) currentClass);
}
@@ -777,7 +777,7 @@
}
// Enums: wire up synthetic name/ordinal params to the super method.
- if (enclosingType instanceof JEnumType) {
+ if (enclosingType.isEnumOrSubclass() != null) {
assert (superOrThisCall != null);
JVariableRef enumNameRef = createVariableRef(
superOrThisCall.getSourceInfo(), ctor.params.get(0));
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index fc42d01..1e43e8c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -960,51 +960,10 @@
}
@Override
- public void endVisit(JsniMethodBody x, Context ctx) {
- JsFunction jsFunc = x.getFunc();
-
- // replace all JSNI idents with a real JsName now that we know it
- new JsModVisitor() {
-
- @Override
- public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
- String ident = x.getIdent();
- if (ident.charAt(0) == '@') {
- HasEnclosingType node = program.jsniMap.get(ident);
- assert (node != null);
- if (node instanceof JField) {
- JField field = (JField) node;
- JsName jsName = names.get(field);
- assert (jsName != null);
- x.resolve(jsName);
-
- // See if we need to add a clinit call to a static field ref
- JsInvocation clinitCall = maybeCreateClinitCall(field);
- if (clinitCall != null) {
- JsExpression commaExpr = createCommaExpression(clinitCall, x);
- ctx.replaceMe(commaExpr);
- }
- } else {
- JMethod method = (JMethod) node;
- if (x.getQualifier() == null) {
- JsName jsName = names.get(method);
- assert (jsName != null);
- x.resolve(jsName);
- } else {
- JsName jsName = polymorphicNames.get(method);
- if (jsName == null) {
- // this can occur when JSNI references an instance method on a
- // type that was never actually instantiated.
- jsName = nullMethodName;
- }
- x.resolve(jsName);
- }
- }
- }
- }
- }.accept(jsFunc);
-
- push(jsFunc);
+ public void endVisit(JsniMethodRef x, Context ctx) {
+ JMethod method = x.getTarget();
+ JsNameRef nameRef = names.get(method).makeRef();
+ push(nameRef);
}
@Override
@@ -1101,6 +1060,57 @@
}
@Override
+ public boolean visit(JsniMethodBody x, Context ctx) {
+ JsFunction jsFunc = x.getFunc();
+
+ // replace all JSNI idents with a real JsName now that we know it
+ new JsModVisitor() {
+
+ @Override
+ public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
+ String ident = x.getIdent();
+ if (ident.charAt(0) == '@') {
+ HasEnclosingType node = program.jsniMap.get(ident);
+ assert (node != null);
+ if (node instanceof JField) {
+ JField field = (JField) node;
+ JsName jsName = names.get(field);
+ assert (jsName != null);
+ x.resolve(jsName);
+
+ // See if we need to add a clinit call to a static field ref
+ JsInvocation clinitCall = maybeCreateClinitCall(field);
+ if (clinitCall != null) {
+ JsExpression commaExpr = createCommaExpression(clinitCall, x);
+ ctx.replaceMe(commaExpr);
+ }
+ } else {
+ JMethod method = (JMethod) node;
+ if (x.getQualifier() == null) {
+ JsName jsName = names.get(method);
+ assert (jsName != null);
+ x.resolve(jsName);
+ } else {
+ JsName jsName = polymorphicNames.get(method);
+ if (jsName == null) {
+ // this can occur when JSNI references an instance method on a
+ // type that was never actually instantiated.
+ jsName = nullMethodName;
+ }
+ x.resolve(jsName);
+ }
+ }
+ }
+ }
+ }.accept(jsFunc);
+
+ push(jsFunc);
+
+ // Do NOT visit JsniMethodRefs/JsniFieldRefs.
+ return false;
+ }
+
+ @Override
public boolean visit(JSwitchStatement x, Context ctx) {
/*
* What a pain.. JSwitchStatement and JsSwitch are modeled completely
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 faad204..97b2468 100644
--- a/user/super/com/google/gwt/emul/java/lang/Class.java
+++ b/user/super/com/google/gwt/emul/java/lang/Class.java
@@ -15,6 +15,8 @@
*/
package java.lang;
+import com.google.gwt.core.client.JavaScriptObject;
+
/**
* Generally unsupported. This class is provided so that the GWT compiler can
* choke down class literal references.
@@ -62,12 +64,13 @@
* @skip
*/
static <T> Class<T> createForEnum(String packageName, String className,
- Class<? super T> superclass) {
+ Class<? super T> superclass, JavaScriptObject enumConstantsFunc) {
// Initialize here to avoid method inliner
Class<T> clazz = new Class<T>();
clazz.typeName = packageName + className;
clazz.modifiers = ENUM;
clazz.superclass = superclass;
+ clazz.enumConstantsFunc = enumConstantsFunc;
return clazz;
}
@@ -97,6 +100,9 @@
return clazz;
}
+ @SuppressWarnings("unused")
+ private JavaScriptObject enumConstantsFunc;
+
private int modifiers;
private String typeName;
@@ -111,10 +117,10 @@
private Class() {
}
- public T[] getEnumConstants() {
- // TODO
- return null;
- }
+ public native T[] getEnumConstants() /*-{
+ return this.@java.lang.Class::enumConstantsFunc
+ && (this.@java.lang.Class::enumConstantsFunc)();
+ }-*/;
public String getName() {
return typeName;
diff --git a/user/test/com/google/gwt/dev/jjs/test/ClassObjectTest.java b/user/test/com/google/gwt/dev/jjs/test/ClassObjectTest.java
index 7cd0685..1391ea2 100644
--- a/user/test/com/google/gwt/dev/jjs/test/ClassObjectTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/ClassObjectTest.java
@@ -23,7 +23,12 @@
public class ClassObjectTest extends GWTTestCase {
private static enum Bar {
- BAR;
+ BAR, BAZ {
+ @Override
+ public String toString() {
+ return "BAZ!";
+ }
+ };
}
private static class Foo implements IFoo {
@@ -32,6 +37,13 @@
private static interface IFoo {
}
+ private static void assertArrayEquals(Object[] expected, Object[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; ++i) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
public String getModuleName() {
return "com.google.gwt.dev.jjs.CompilerSuite";
}
@@ -48,6 +60,7 @@
assertFalse(o.getClass().isEnum());
assertFalse(o.getClass().isInterface());
assertFalse(o.getClass().isPrimitive());
+ assertNull(o.getClass().getEnumConstants());
Foo[][] f = new Foo[3][3];
assertEquals(Foo[][].class, f.getClass());
@@ -66,6 +79,7 @@
assertFalse(Foo.class.isEnum());
assertFalse(Foo.class.isInterface());
assertFalse(Foo.class.isPrimitive());
+ assertNull(o.getClass().getEnumConstants());
}
public void testEnum() {
@@ -80,6 +94,24 @@
assertTrue(o.getClass().isEnum());
assertFalse(o.getClass().isInterface());
assertFalse(o.getClass().isPrimitive());
+ assertArrayEquals(Bar.values(), o.getClass().getEnumConstants());
+ }
+
+ public void testEnumSubclass() {
+ Object o = Bar.BAZ;
+ assertNotSame(Bar.class, o.getClass());
+ assertEquals(Bar.class, o.getClass().getSuperclass());
+ /*
+ * TODO: implement
+ */
+ // assertEquals(Bar.class, o.getClass().getDeclaringClass());
+ assertTrue(o.getClass().getName().endsWith("$1"));
+ assertTrue(o.getClass().toString().endsWith("$1"));
+ assertFalse(o.getClass().isArray());
+ assertFalse(o.getClass().isEnum());
+ assertFalse(o.getClass().isInterface());
+ assertFalse(o.getClass().isPrimitive());
+ assertNull(o.getClass().getEnumConstants());
}
public void testInterface() {
@@ -92,6 +124,7 @@
assertFalse(IFoo.class.isEnum());
assertTrue(IFoo.class.isInterface());
assertFalse(IFoo.class.isPrimitive());
+ assertNull(IFoo.class.getEnumConstants());
}
public void testPrimitive() {
@@ -102,6 +135,7 @@
assertFalse(int.class.isEnum());
assertFalse(int.class.isInterface());
assertTrue(int.class.isPrimitive());
+ assertNull(int.class.getEnumConstants());
}
}