Add tests for @JsExport and @JsType on Enum type.
Change-Id: Iace8a165d4679f22918822a575e713024075a2c7
diff --git a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
index eda8c19..45b800c 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
@@ -69,6 +69,8 @@
"@JsExport and @JsNoExport is not allowed at the same time.";
public static final String ERR_JSPROPERTY_ONLY_BEAN_OR_FLUENT_STYLE_NAMING =
"@JsProperty is only allowed on JavaBean-style or fluent-style named methods";
+ public static final String ERR_JSEXPORT_ON_ENUMERATION =
+ "@JsExport is not allowed on individual enumerations";
public static final String ERR_MUST_EXTEND_MAGIC_PROTOTYPE_CLASS =
"Classes implementing @JsType with a prototype must extend that interface's Prototype class";
public static final String ERR_CLASS_EXTENDS_MAGIC_PROTOTYPE_BUT_NO_PROTOTYPE_ATTRIBUTE =
@@ -178,7 +180,7 @@
@Override
public void endVisit(FieldDeclaration field, MethodScope scope) {
- checkJsExport(field.binding);
+ checkJsExport(field);
if (!isJso()) {
return;
@@ -275,8 +277,12 @@
}
}
- private void checkJsExport(FieldBinding fb) {
+ private void checkJsExport(FieldDeclaration fd) {
+ FieldBinding fb = fd.binding;
if (JdtUtil.getAnnotation(fb, JsInteropUtil.JSEXPORT_CLASS) != null) {
+ if (isEnumConstant(fd)) {
+ errorOn(fb, ERR_JSEXPORT_ON_ENUMERATION);
+ }
if (!areAllEnclosingClassesPublic() || !fb.isStatic() || !fb.isFinal() || !fb.isPublic()) {
errorOn(fb, ERR_JSEXPORT_ONLY_CTORS_STATIC_METHODS_AND_STATIC_FINAL_FIELDS);
}
@@ -286,6 +292,11 @@
}
}
+ private boolean isEnumConstant(FieldDeclaration fd) {
+ return (fd.initialization != null && fd.initialization instanceof AllocationExpression
+ && ((AllocationExpression) fd.initialization).enumConstant != null);
+ }
+
private void checkJsProperty(MethodBinding mb, boolean allowed) {
AnnotationBinding jsProperty = JdtUtil.getAnnotation(mb, JsInteropUtil.JSPROPERTY_CLASS);
if (jsProperty != null) {
diff --git a/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
index bb6fa2d..81f6b24 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JSORestrictionsTest.java
@@ -415,6 +415,24 @@
shouldGenerateNoError(goodCode);
}
+ public void testJsExportOnEnum() {
+ StringBuilder goodCode = new StringBuilder();
+ goodCode.append("import com.google.gwt.core.client.js.JsExport;\n");
+ goodCode.append("@JsExport enum Buggy { TEST1, TEST2;}");
+
+ shouldGenerateNoError(goodCode);
+ }
+
+ public void testJsExportNotOnEnumeration() {
+ StringBuilder buggyCode = new StringBuilder();
+ buggyCode.append("import com.google.gwt.core.client.js.JsExport;\n");
+ buggyCode.append("public enum Buggy {\n");
+ buggyCode.append(" @JsExport TEST1, TEST2;\n;");
+ buggyCode.append("}");
+
+ shouldGenerateError(buggyCode, "Line 3: " + JSORestrictionsChecker.ERR_JSEXPORT_ON_ENUMERATION);
+ }
+
public void testJsExportNotOnNonPublicClass() {
StringBuilder buggyCode = new StringBuilder();
buggyCode.append("import com.google.gwt.core.client.js.JsExport;\n");
diff --git a/user/test/com/google/gwt/core/client/interop/JsExportTest.java b/user/test/com/google/gwt/core/client/interop/JsExportTest.java
index 822b3cf..b832af1 100644
--- a/user/test/com/google/gwt/core/client/interop/JsExportTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsExportTest.java
@@ -103,7 +103,8 @@
return $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_1
|| $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_2
|| $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_3
- || $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_4;
+ || $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_4
+ || $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_5;
}-*/;
private native Object getNotExportedMethods() /*-{
@@ -135,4 +136,96 @@
private static native String getEnumNameViaJs(MyClassWithNestedEnum.NestedEnum ref) /*-{
return ref.name2();
}-*/;
+
+ public void testEnum_enumerations() {
+ assertNotNull(getEnumerationTEST1());
+ assertNotNull(getEnumerationTEST2());
+ }
+
+ private static native Object getEnumerationTEST1() /*-{
+ return $wnd.woo.MyEnumWithJsExport.TEST1;
+ }-*/;
+
+ private static native Object getEnumerationTEST2() /*-{
+ return $wnd.woo.MyEnumWithJsExport.TEST2;
+ }-*/;
+
+ public void testEnum_exportedMethods() {
+ assertNotNull(getPublicStaticMethodInEnum());
+ }
+
+ private static native Object getPublicStaticMethodInEnum() /*-{
+ return $wnd.woo.MyEnumWithJsExport.publicStaticMethod();
+ }-*/;
+
+ public void testEnum_exportedFields() {
+ assertEquals(1, getPublicStaticFinalFieldInEnum());
+
+ // explicitly marked @JsExport fields must be final
+ // but ones that are in a @JsExported class don't need to be final
+ assertEquals(2, getPublicStaticFieldInEnum());
+ }
+
+ private static native int getPublicStaticFinalFieldInEnum() /*-{
+ return $wnd.woo.MyEnumWithJsExport.publicStaticFinalField;
+ }-*/;
+
+ private static native int getPublicStaticFieldInEnum() /*-{
+ return $wnd.woo.MyEnumWithJsExport.publicStaticField;
+ }-*/;
+
+ public void testEnum_notExported() {
+ assertNull(getNotExportedFieldsInEnum());
+ assertNull(getNotExportedMethodsInEnum());
+ }
+
+ private native Object getNotExportedFieldsInEnum() /*-{
+ return $wnd.woo.MyEnumWithJsExport.publicFinalField
+ || $wnd.woo.MyEnumWithJsExport.privateStaticFinalField
+ || $wnd.woo.MyEnumWithJsExport.protectedStaticFinalField
+ || $wnd.woo.MyEnumWithJsExport.defaultStaticFinalField;
+ }-*/;
+
+ private native Object getNotExportedMethodsInEnum() /*-{
+ return $wnd.woo.MyEnumWithJsExport.publicMethod
+ || $wnd.woo.MyEnumWithJsExport.protectedStaticMethod
+ || $wnd.woo.MyEnumWithJsExport.privateStaticMethod
+ || $wnd.woo.MyEnumWithJsExport.defaultStaticMethod;
+ }-*/;
+
+ public void testEnum_subclassEnumerations() {
+ assertNotNull(getEnumerationA());
+ assertNotNull(getEnumerationB());
+ assertNotNull(getEnumerationC());
+ }
+
+ private static native Object getEnumerationA() /*-{
+ return $wnd.woo.MyEnumWithSubclassGen.A;
+ }-*/;
+
+ private static native Object getEnumerationB() /*-{
+ return $wnd.woo.MyEnumWithSubclassGen.B;
+ }-*/;
+
+ private static native Object getEnumerationC() /*-{
+ return $wnd.woo.MyEnumWithSubclassGen.C;
+ }-*/;
+
+ public void testEnum_subclassMethodCallFromExportedEnumerations() {
+ assertEquals(100, callPublicMethodFromEnumerationA());
+ assertEquals(200, callPublicMethodFromEnumerationB());
+ assertEquals(1, callPublicMethodFromEnumerationC());
+ }
+
+ private static native int callPublicMethodFromEnumerationA() /*-{
+ return $wnd.woo.MyEnumWithSubclassGen.A.foo();
+ }-*/;
+
+ private static native int callPublicMethodFromEnumerationB() /*-{
+ return $wnd.woo.MyEnumWithSubclassGen.B.foo();
+ }-*/;
+
+ private static native int callPublicMethodFromEnumerationC() /*-{
+ return $wnd.woo.MyEnumWithSubclassGen.C.foo();
+ }-*/;
}
diff --git a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
index d68357f..1c34d63 100644
--- a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
@@ -80,17 +80,9 @@
public void testConcreteJsTypeAccess() {
ConcreteJsType concreteJsType = new ConcreteJsType();
- assertTrue(hasField(concreteJsType, "publicMethod"));
- assertTrue(hasField(concreteJsType, "publicField"));
-
- assertFalse(hasField(concreteJsType, "publicStaticMethod"));
- assertFalse(hasField(concreteJsType, "privateMethod"));
- assertFalse(hasField(concreteJsType, "protectedMethod"));
- assertFalse(hasField(concreteJsType, "packageMethod"));
- assertFalse(hasField(concreteJsType, "publicStaticField"));
- assertFalse(hasField(concreteJsType, "privateField"));
- assertFalse(hasField(concreteJsType, "protectedField"));
- assertFalse(hasField(concreteJsType, "packageField"));
+ testJsTypeHasFields(concreteJsType, "publicMethod", "publicField");
+ testJsTypeHasNoFields(concreteJsType, "publicStaticMethod", "privateMethod", "protectedMethod",
+ "packageMethod", "publicStaticField", "privateField", "protectedField", "packageField");
}
public void testConcreteJsTypeSubclassAccess() {
@@ -98,20 +90,14 @@
ConcreteJsTypeSubclass concreteJsTypeSubclass = new ConcreteJsTypeSubclass();
// A subclass of a JsType is not itself a JsType.
- assertFalse(hasField(concreteJsTypeSubclass, "publicSubclassMethod"));
- assertFalse(hasField(concreteJsTypeSubclass, "publicSubclassField"));
- assertFalse(hasField(concreteJsTypeSubclass, "publicStaticSubclassMethod"));
- assertFalse(hasField(concreteJsTypeSubclass, "privateSubclassMethod"));
- assertFalse(hasField(concreteJsTypeSubclass, "protectedSubclassMethod"));
- assertFalse(hasField(concreteJsTypeSubclass, "packageSubclassMethod"));
- assertFalse(hasField(concreteJsTypeSubclass, "publicStaticSubclassField"));
- assertFalse(hasField(concreteJsTypeSubclass, "privateSubclassField"));
- assertFalse(hasField(concreteJsTypeSubclass, "protectedSubclassField"));
- assertFalse(hasField(concreteJsTypeSubclass, "packageSubclassField"));
+ testJsTypeHasNoFields(concreteJsTypeSubclass, "publicSubclassMethod", "publicSubclassField",
+ "publicStaticSubclassMethod", "privateSubclassMethod", "protectedSubclassMethod",
+ "packageSubclassMethod", "publicStaticSubclassField", "privateSubclassField",
+ "protectedSubclassField", "packageSubclassField");
// But if it overrides an exported method then the overriding method will be exported.
- assertTrue(hasField(concreteJsType, "publicMethod"));
- assertTrue(hasField(concreteJsTypeSubclass, "publicMethod"));
+ testJsTypeHasFields(concreteJsType, "publicMethod");
+ testJsTypeHasFields(concreteJsTypeSubclass, "publicMethod");
assertFalse(
areSameFunction(concreteJsType, "publicMethod", concreteJsTypeSubclass, "publicMethod"));
assertFalse(callIntFunction(concreteJsType, "publicMethod")
@@ -208,6 +194,24 @@
assertFalse(obj2 instanceof MyNamespacedJsInterface);
}
+ public void testEnumeration() {
+ assertEquals(2, callPublicMethodFromEnumeration(MyEnumWithJsType.TEST1));
+ assertEquals(3, callPublicMethodFromEnumeration(MyEnumWithJsType.TEST2));
+ }
+
+ public void testEnumJsTypeAccess() {
+ testJsTypeHasFields(MyEnumWithJsType.TEST2, "publicMethod", "publicField");
+ testJsTypeHasNoFields(MyEnumWithJsType.TEST2, "publicStaticMethod", "privateMethod",
+ "protectedMethod", "packageMethod", "publicStaticField", "privateField", "protectedField",
+ "packageField");
+ }
+
+ public void testEnumSubclassEnumeration() {
+ assertEquals(100, callPublicMethodFromEnumerationSubclass(MyEnumWithSubclassGen.A));
+ assertEquals(200, callPublicMethodFromEnumerationSubclass(MyEnumWithSubclassGen.B));
+ assertEquals(1, callPublicMethodFromEnumerationSubclass(MyEnumWithSubclassGen.C));
+ }
+
private static native boolean alwaysTrue() /*-{
return !!$wnd;
}-*/;
@@ -252,4 +256,25 @@
private static native boolean isFirefox40OrEarlier() /*-{
return @com.google.gwt.dom.client.DOMImplMozilla::isGecko2OrBefore()();
}-*/;
+
+ private static native int callPublicMethodFromEnumeration(MyEnumWithJsType enumeration) /*-{
+ return enumeration.idxAddOne();
+ }-*/;
+
+ private static native int callPublicMethodFromEnumerationSubclass(
+ MyEnumWithSubclassGen enumeration) /*-{
+ return enumeration.foo();
+ }-*/;
+
+ private static void testJsTypeHasFields(Object obj, String... fields) {
+ for (String field : fields) {
+ assertTrue("Field '" + field + "' should be exported", hasField(obj, field));
+ }
+ }
+
+ private static void testJsTypeHasNoFields(Object obj, String... fields) {
+ for (String field : fields) {
+ assertFalse("Field '" + field + "' should not be exported", hasField(obj, field));
+ }
+ }
}
diff --git a/user/test/com/google/gwt/core/client/interop/MyEnumWithJsExport.java b/user/test/com/google/gwt/core/client/interop/MyEnumWithJsExport.java
new file mode 100644
index 0000000..1dfd841
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/MyEnumWithJsExport.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.interop;
+
+import com.google.gwt.core.client.js.JsExport;
+
+/**
+ * This enum is annotated as @JsExport.
+ */
+@JsExport
+public enum MyEnumWithJsExport {
+ TEST1, TEST2;
+
+ public static int publicStaticMethod() {
+ return 0;
+ }
+
+ public static final int publicStaticFinalField = 1;
+
+ // explicitly marked @JsExport fields must be final
+ // but ones that are in a @JsExported class don't need to be final
+ public static int publicStaticField = 2;
+
+ public final int publicFinalField = 3;
+
+ private static final int privateStaticFinalField = 4;
+
+ protected static final int protectedStaticFinalField = 5;
+
+ static final int defaultStaticFinalField = 6;
+
+ public int publicMethod() {
+ return 0;
+ }
+
+ protected static int protectedStaticMethod() {
+ return 0;
+ }
+
+ static int defaultStaticMethod() {
+ return 0;
+ }
+
+ private static int privateStaticMethod() {
+ return 0;
+ }
+}
diff --git a/user/test/com/google/gwt/core/client/interop/MyEnumWithJsType.java b/user/test/com/google/gwt/core/client/interop/MyEnumWithJsType.java
new file mode 100644
index 0000000..7cc67e8
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/MyEnumWithJsType.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.interop;
+
+import com.google.gwt.core.client.js.JsType;
+
+/**
+ * This enum is annotated as a @JsType.
+ */
+@JsType
+public enum MyEnumWithJsType {
+ TEST1(1), TEST2(2);
+
+ public int idx;
+ MyEnumWithJsType(int a) {
+ this.idx = a;
+ }
+
+ public int idxAddOne() {
+ return idx + 1;
+ }
+
+ public int publicMethod() {
+ return 10;
+ }
+
+ public static void publicStaticMethod() {
+ }
+
+ private void privateMethod() {
+ }
+
+ protected void protectedMethod() {
+ }
+
+ void packageMethod() {
+ }
+
+ public int publicField = 10;
+
+ public static int publicStaticField = 10;
+
+ private int privateField = 10;
+
+ protected int protectedField = 10;
+
+ int packageField = 10;
+}
diff --git a/user/test/com/google/gwt/core/client/interop/MyEnumWithSubclassGen.java b/user/test/com/google/gwt/core/client/interop/MyEnumWithSubclassGen.java
new file mode 100644
index 0000000..dccda4d
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/MyEnumWithSubclassGen.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.interop;
+
+import com.google.gwt.core.client.js.JsExport;
+import com.google.gwt.core.client.js.JsType;
+
+/**
+ * This enum is annotated as @JsExport and has enumerations which cause subclass generation.
+ */
+@JsExport
+@JsType
+public enum MyEnumWithSubclassGen {
+
+ A {
+ @Override
+ public int foo() {
+ return 100;
+ }
+ },
+ B {
+ @Override
+ public int foo() {
+ return 200;
+ }
+ },
+ C;
+
+ public int foo() {
+ return 1;
+ }
+}
diff --git a/user/test/com/google/gwt/core/client/interop/StaticInitializerStaticField.java b/user/test/com/google/gwt/core/client/interop/StaticInitializerStaticField.java
index 1bd56d9..4275644 100644
--- a/user/test/com/google/gwt/core/client/interop/StaticInitializerStaticField.java
+++ b/user/test/com/google/gwt/core/client/interop/StaticInitializerStaticField.java
@@ -40,6 +40,9 @@
// Not public
private static final Object NOT_EXPORTED_4 = new Object();
+ // Not public
+ protected static final Object NOT_EXPORTED_5 = new Object();
+
/**
* Test interface that export a static field.
*/