Adds support for static overlay methods.

This patch also makes sure that no JSNI is allowed in native classes.
Although JsOverlay JSNI results in correct code, mixing too concepts
together results in classes that is difficult to reason about.

Change-Id: I397e60d8dd05efdb0a19099fbe8ba49c47d44890
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 4ac2ec7..c0f8ab6 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
@@ -253,6 +253,7 @@
 
     if (method.isJsOverlay()) {
       checkJsOverlay(method);
+      return;
     }
 
     checkUnusableByJs(method);
@@ -362,13 +363,19 @@
       return;
     }
 
-    if (method.isJsNative() || method.isJsniMethod() || method.isStatic() || !method.isFinal()) {
+    if (method.isJsNative() || (!method.isFinal() && !method.isStatic())) {
       logError(method,
-          "JsOverlay method '%s' cannot be non-final, static, nor native.", methodDescription);
+          "JsOverlay method '%s' cannot be non-final nor native.", methodDescription);
     }
   }
 
   private void checkMemberOfNativeJsType(JMember member) {
+    if (member instanceof JMethod && ((JMethod) member).isJsniMethod()) {
+      logError(member, "JSNI method %s is not allowed in a native JsType.",
+          getMemberDescription(member));
+      return;
+    }
+
     if (member.isSynthetic() || member.isJsNative() || member.isJsOverlay()) {
       return;
     }
@@ -376,18 +383,10 @@
     if (member.getJsName() == null) {
       logError(member, "Native JsType member %s is not public or has @JsIgnore.",
           getMemberDescription(member));
-      return;
+    } else {
+      logError(member, "Native JsType method %s should be native or abstract.",
+          getMemberDescription(member));
     }
-
-    JMethod method = (JMethod) member;
-    if (method.isJsniMethod()) {
-      logError(method, "JSNI method %s is not allowed in a native JsType.",
-          getMemberDescription(method));
-      return;
-    }
-
-    logError(method, "Native JsType method %s should be native or abstract.",
-        getMemberDescription(method));
   }
 
   private void checkMemberQualifiedJsName(JMember member) {
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 2e0306a..9aaf79f 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
@@ -1356,6 +1356,7 @@
     addSnippetClassDecl(
         "@JsType(isNative=true) public static class Buggy {",
         "  @JsOverlay public final void m() { }",
+        "  @JsOverlay public final void m(int x) { }",
         "  @JsOverlay private final void n() { }",
         "  @JsOverlay final void o() { }",
         "  @JsOverlay protected final void p() { }",
@@ -1364,16 +1365,17 @@
     assertBuggySucceeds();
   }
 
-  public void testJsOverlayOnStaticFails() {
+  public void testJsOverlayOnStaticSucceds() throws Exception {
     addSnippetImport("jsinterop.annotations.JsType");
     addSnippetImport("jsinterop.annotations.JsOverlay");
     addSnippetClassDecl(
         "@JsType(isNative=true) public static class Buggy {",
-        "  @JsOverlay public static final void m() { }",
+        "  @JsOverlay public static void m() { }",
+        "  @JsOverlay public static void m(int x) { }",
+        "  @JsOverlay private static void m(boolean x) { }",
         "}");
 
-    assertBuggyFails("Line 6: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be "
-        + "non-final, static, nor native.");
+    assertBuggySucceeds();
   }
 
   public void testJsOverlayImplementingInterfaceMethodFails() {
@@ -1415,8 +1417,7 @@
         "}");
 
     assertBuggyFails(
-        "Line 6: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be non-final, "
-            + "static, nor native.");
+        "Line 6: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be non-final nor native.");
   }
 
   public void testJsOverlayOnNativeMethodFails() {
@@ -1424,12 +1425,17 @@
     addSnippetImport("jsinterop.annotations.JsOverlay");
     addSnippetClassDecl(
         "@JsType(isNative=true) public static class Buggy {",
-        "  @JsOverlay public final native void m();",
+        "  @JsOverlay public static final native void m1();",
+        "  @JsOverlay public static final native void m2()/*-{}-*/;",
+        "  @JsOverlay public final native void m3();",
+        "  @JsOverlay public final native void m4()/*-{}-*/;",
         "}");
 
     assertBuggyFails(
-        "Line 6: JsOverlay method 'void EntryPoint.Buggy.m()' cannot be non-final, "
-            + "static, nor native.");
+        "Line 6: JsOverlay method 'void EntryPoint.Buggy.m1()' cannot be non-final nor native.",
+        "Line 7: JSNI method 'void EntryPoint.Buggy.m2()' is not allowed in a native JsType.",
+        "Line 8: JsOverlay method 'void EntryPoint.Buggy.m3()' cannot be non-final nor native.",
+        "Line 9: JSNI method 'void EntryPoint.Buggy.m4()' is not allowed in a native JsType.");
   }
 
   public void testJsOverlayOnJsoMethodSucceeds() throws Exception {
@@ -1593,12 +1599,29 @@
     assertBuggySucceeds();
   }
 
-  public void testNativeJsTypeMutlipleConstructorSucceeds() throws Exception {
+  public void testNativeJsTypeOverloadsSucceeds() throws Exception {
     addSnippetImport("jsinterop.annotations.JsType");
     addSnippetClassDecl(
         "@JsType(isNative=true) static class Buggy {",
-        "  public Buggy(int i) { }",
+        "  public static native void m();",
+        "  public static native void m(Object o);",
+        "  public static native void m(String o);",
         "  public Buggy() { }",
+        "  public Buggy(Object o) { }",
+        "  public Buggy(String o) { }",
+        "  public native void n();",
+        "  public native void n(Object o);",
+        "  public native void n(String o);",
+        "}");
+
+    assertBuggySucceeds();
+  }
+
+  public void testNativeJsTypeAbstractMethodSucceeds() throws Exception {
+    addSnippetImport("jsinterop.annotations.JsType");
+    addSnippetClassDecl(
+        "@JsType(isNative=true) static abstract class Buggy {",
+        "  public abstract void m(Object o);",
         "}");
 
     assertBuggySucceeds();
@@ -1623,30 +1646,6 @@
     assertBuggySucceeds();
   }
 
-  public void testNativeJsTypeInstanceMethodOverloadSucceeds() throws Exception {
-    addSnippetImport("jsinterop.annotations.JsType");
-    addSnippetClassDecl(
-        "@SuppressWarnings(\"unusable-by-js\")",
-        "@JsType(isNative=true) public static class Buggy {",
-        "  public native void m(Object o);",
-        "  public native void m(Object[] o);",
-        "}");
-
-    assertBuggySucceeds();
-  }
-
-  public void testNativeJsTypeStaticMethodOverloadSucceeds() throws Exception {
-    addSnippetImport("jsinterop.annotations.JsType");
-    addSnippetClassDecl(
-        "@SuppressWarnings(\"unusable-by-js\")",
-        "@JsType(isNative=true) public static class Buggy {",
-        "  public static native void m(Object o);",
-        "  public static native void m(Object[] o);",
-        "}");
-
-    assertBuggySucceeds();
-  }
-
   public void testNonJsTypeExtendingNativeJsTypeWithInstanceMethodSucceeds() throws Exception {
     addSnippetImport("jsinterop.annotations.JsType");
     addSnippetClassDecl(
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 ef16a5f..7d8a005 100644
--- a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
@@ -23,6 +23,7 @@
 
 import java.util.Iterator;
 
+import javaemul.internal.annotations.DoNotInline;
 import jsinterop.annotations.JsFunction;
 import jsinterop.annotations.JsOverlay;
 import jsinterop.annotations.JsPackage;
@@ -504,13 +505,21 @@
     object[name] = value;
   }-*/;
 
-  @JsType(isNative = true)
+  @JsType(isNative = true, namespace = GLOBAL, name = "Object")
   static class NativeJsTypeWithOverlay {
-    public native int m();
 
-    @JsOverlay
-    public final int callM() {
-      return m();
+    public static native String[] keys(Object o);
+
+    @JsOverlay @DoNotInline
+    public static final boolean hasM(Object obj) {
+      return keys(obj)[0].equals("m");
+    }
+
+    public native boolean hasOwnProperty(String name);
+
+    @JsOverlay @DoNotInline
+    public final boolean hasM() {
+      return hasOwnProperty("m");
     }
   }
 
@@ -520,6 +529,7 @@
 
   public void testNativeJsTypeWithOverlay() {
     NativeJsTypeWithOverlay object = createNativeJsTypeWithOverlay();
-    assertEquals(6, object.callM());
+    assertTrue(object.hasM());
+    assertTrue(NativeJsTypeWithOverlay.hasM(object));
   }
 }