Disallow @JsType in local classes and native @JsType in proper inner classes.
Change-Id: I1478898d0f2ddddefdcbb895e0d336685ee9cbc8
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 f8c997a..8222bbb 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
@@ -1458,8 +1458,7 @@
continue;
}
- if (type.isJsType() && !type.getClassDisposition().isLocalType()) {
- // only types with explicit source names in Java may have an exported prototype
+ if (type.isJsType()) {
exportedMembersByExportName.put(type.getQualifiedJsName(), type);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
index 328936e..8717ee0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
@@ -25,6 +25,7 @@
import com.google.gwt.dev.jjs.ast.JConstructor;
import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType.NestedClassDisposition;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JExpressionStatement;
import com.google.gwt.dev.jjs.ast.JField;
@@ -413,12 +414,33 @@
}.accept(jprogram);
}
- private void checkNativeJsType(JDeclaredType type) {
+ private boolean checkJsType(JDeclaredType type) {
+ // Java (at least up to Java 8) does not allow to annotate anonymous classes or lambdas; if
+ // it ever becomes possible we should emit an error.
+ assert type.getClassDisposition() != NestedClassDisposition.ANONYMOUS
+ && type.getClassDisposition() != NestedClassDisposition.LAMBDA;
+
+ if (type.getClassDisposition() == NestedClassDisposition.LOCAL) {
+ logError("Local class '%s' cannot be a JsType.", type);
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean checkNativeJsType(JDeclaredType type) {
// TODO(rluble): add inheritance restrictions.
+
if (type.isEnumOrSubclass() != null) {
logError("Enum '%s' cannot be a native JsType.", type);
- return;
+ return false;
}
+
+ if (type.getClassDisposition() == NestedClassDisposition.INNER) {
+ logError("Non static inner class '%s' cannot be a native JsType.", type);
+ return false;
+ }
+
if (!isClinitEmpty(type)) {
logError("Native JsType '%s' cannot have static initializer.", type);
}
@@ -429,6 +451,7 @@
getMemberDescription(constructor));
}
}
+ return true;
}
private void checkJsFunction(JDeclaredType type) {
@@ -489,8 +512,15 @@
private void checkType(JDeclaredType type) {
minimalRebuildCache.removeExportedNames(type.getName());
+ if (type.isJsType()) {
+ if (!checkJsType(type)) {
+ return;
+ }
+ }
if (type.isJsNative()) {
- checkNativeJsType(type);
+ if (!checkNativeJsType(type)) {
+ return;
+ }
}
if (type.isJsFunction()) {
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
index 259daaa..c17ca62 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
@@ -1156,6 +1156,32 @@
"Line 4: Enum 'EntryPoint.Buggy' cannot be a native JsType.");
}
+ public void testInnerNativeJsTypeFails() {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@JsType(isNative=true) public class Buggy { }");
+
+ assertBuggyFails(
+ "Line 4: Non static inner class 'EntryPoint.Buggy' cannot be a native JsType.");
+ }
+
+ public void testInnerJsTypeSucceeds() throws Exception {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "@SuppressWarnings(\"unusable-by-js\") @JsType public class Buggy { }");
+
+ assertBuggySucceeds();
+ }
+
+ public void testLocalJsTypeFails() {
+ addSnippetImport("jsinterop.annotations.JsType");
+ addSnippetClassDecl(
+ "public class Buggy { void m() { @JsType class Local {} } }");
+
+ assertBuggyFails(
+ "Line 4: Local class 'EntryPoint.Buggy.1Local' cannot be a JsType.");
+ }
+
public void testNativeJsTypeInterfaceCompileTimeConstantSucceeds() throws Exception {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetClassDecl(
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
index 311abbd..0fdbbc3 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
@@ -314,27 +314,28 @@
* <p>
* Typetightener used to incorrectly tighten method calls marked with STATIC_DISPATCH_ONLY.
*/
+
public void testIncorrectDispatch() {
- final int[] state = new int[1];
-
- @JsType
- abstract class A {
- public void m() {
- state[0] = 1;
- }
- }
-
- @JsType
- class B extends A {
- public void m() {
- super.m();
- }
- }
-
+ state = new int[1];
new B().m();
assertEquals(1, state[0]);
}
+ static int[] state;
+ @JsType
+ abstract static class A {
+ public void m() {
+ state[0] = 1;
+ }
+ }
+
+ @JsType
+ static class B extends A {
+ public void m() {
+ super.m();
+ }
+ }
+
private static void assertEqualContents(float[] expected, float[] actual) {
assertEquals("Array length mismatch", expected.length, actual.length);