Implement @JsOverlay with default methods in Java8.

Change-Id: Ie7bcdc86b44a7fe33b6ba2e273d2c23351810354
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 250e7f8..6a850ff 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -249,8 +249,9 @@
 
   @Override
   public boolean isJsOverlay() {
-    return isJsOverlay || getEnclosingType().isJsoType() ||
-        getEnclosingType().isJsNative() && JProgram.isClinit(this);
+    return isJsOverlay
+        || getEnclosingType().isJsoType()
+        || getEnclosingType().isJsNative() && JProgram.isClinit(this);
   }
 
   public void setSyntheticAccidentalOverride() {
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 06c84e1..4a15b35 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
@@ -239,6 +239,10 @@
       checkMemberOfNativeJsType(member);
     }
 
+    if (member.needsDynamicDispatch()) {
+      checkIllegalOverrides(member);
+    }
+
     if (member.isJsOverlay()) {
       checkJsOverlay(member);
       return;
@@ -271,6 +275,22 @@
     }
   }
 
+  private void checkIllegalOverrides(JMember member) {
+    if (member instanceof JField) {
+      return;
+    }
+
+    JMethod method = (JMethod) member;
+    for (JMethod overriddeMethod : method.getOverriddenMethods()) {
+      if (overriddeMethod.isJsOverlay()) {
+        logError(member, "Method '%s' cannot override a JsOverlay method '%s'.",
+            JjsUtils.getReadableDescription(method),
+            JjsUtils.getReadableDescription(overriddeMethod));
+        return;
+      }
+    }
+  }
+
   private void checkJsOverlay(JMember member) {
     if (member.getEnclosingType().isJsoType()) {
       return;
@@ -299,7 +319,8 @@
       return;
     }
 
-    if (method.getBody() == null || (!method.isFinal() && !method.isStatic())) {
+    if (method.getBody() == null || (!method.isFinal() && !method.isStatic()
+        && !method.isDefaultMethod())) {
       logError(member,
           "JsOverlay method '%s' cannot be non-final nor native.", methodDescription);
     }
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 723c155..abbff0b 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
@@ -1417,21 +1417,40 @@
         "Line 6: Native JsType ''EntryPoint.Buggy'' can only extend native JsType interfaces.");
   }
 
-  public void testNativeJsTypeInterfaceDefenderMethodsFails() throws Exception {
+  public void testNativeJsTypeInterfaceDefenderMethodsFails() {
+    addSnippetImport("jsinterop.annotations.JsType");
+    addSnippetImport("jsinterop.annotations.JsOverlay");
+    addSnippetClassDecl(
+        "@JsType(isNative=true) public interface Interface {",
+        "  @JsOverlay default void someOtherMethod(){}",
+        "}",
+        "public static class OtherClass implements Interface {",
+        "  public void someOtherMethod() {}",
+        "}",
+        "@JsType(isNative=true) public interface Buggy extends Interface {",
+        "  default void someMethod(){}",
+        "  void someOtherMethod();",
+        "}");
+
+    assertBuggyFails(
+        "Line 9: Method 'void EntryPoint.OtherClass.someOtherMethod()' cannot override a "
+            + "JsOverlay method 'void EntryPoint.Interface.someOtherMethod()'.",
+        "Line 12: Native JsType method 'void EntryPoint.Buggy.someMethod()' should be native "
+            + "or abstract.",
+        "Line 13: Method 'void EntryPoint.Buggy.someOtherMethod()' cannot override a JsOverlay"
+            + " method 'void EntryPoint.Interface.someOtherMethod()'.");
+  }
+
+  public void testJsOverlayOnNativeJsTypeInterfaceSucceds() throws Exception {
     addSnippetImport("jsinterop.annotations.JsType");
     addSnippetImport("jsinterop.annotations.JsOverlay");
     addSnippetClassDecl(
         "@JsType(isNative=true) public interface Buggy {",
-        "  default void someMethod(){}",
-        "  @JsOverlay",
-        "  default void someOverlayMethod(){}",
+        "  @JsOverlay Object obj = new Object();",
+        "  @JsOverlay default void someOverlayMethod(){}",
         "}");
 
-    assertBuggyFails(
-        "Line 6: Native JsType method 'void EntryPoint.Buggy.someMethod()' should be native "
-            + "or abstract.",
-        "Line 8: JsOverlay method 'void EntryPoint.Buggy.someOverlayMethod()' cannot be "
-            + "non-final nor native.");
+    assertBuggySucceeds();
   }
 
   public void testJsOverlayOnNativeJsTypeMemberSucceeds() throws Exception {
@@ -1439,7 +1458,7 @@
     addSnippetImport("jsinterop.annotations.JsOverlay");
     addSnippetClassDecl(
         "@JsType(isNative=true) public static class Buggy {",
-        "  @JsOverlay public static final int f = 2;",
+        "  @JsOverlay public static Object object = new Object();",
         "  @JsOverlay public static void m() { }",
         "  @JsOverlay public static void m(int x) { }",
         "  @JsOverlay private static void m(boolean x) { }",
diff --git a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
index 8195c77..6b829f0 100644
--- a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -25,6 +25,7 @@
 import java.util.List;
 
 import jsinterop.annotations.JsOverlay;
+import jsinterop.annotations.JsProperty;
 import jsinterop.annotations.JsType;
 
 /**
@@ -1306,23 +1307,24 @@
     }
   }
 
-// TODO(rluble): uncomment when we allow @JsOverlay methods to be default methods of
-// interfaces
-//  @JsType(isNative = true)
-//  interface NativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod {
-//  @JsOverlay
-//    Object object = new Integer(5);
-//
-//    @JsOverlay
-//    default Object getObject() {
-//      return object;
-//    }
-//  }
-//
-//  private native NativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod
-//      createNativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod() /*-{
-//    return {};
-//  }-*/;
+  @JsType(isNative = true)
+  interface NativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod {
+    @JsOverlay
+    Object object = new Integer(5);
+
+    @JsProperty
+    int getA();
+
+    @JsOverlay
+    default Object getObject() {
+      return ((int) object) + this.getA();
+    }
+  }
+
+  private native NativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod
+      createNativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod() /*-{
+    return {a: 1};
+  }-*/;
 
   @JsType(isNative = true)
   interface NativeJsTypeInterfaceWithStaticInitialization {
@@ -1340,11 +1342,9 @@
     assertEquals(3, NativeJsTypeInterfaceWithStaticInitializationAndFieldAccess.object);
     assertEquals(
         4, NativeJsTypeInterfaceWithStaticInitializationAndStaticOverlayMethod.getObject());
-// TODO(rluble): uncomment when we allow @JsOverlay methods to be default methods of
-// interfaces
-//    assertEquals(new Integer(5),
-//        createNativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod()
-//            .getObject());
+    assertEquals(6,
+        createNativeJsTypeInterfaceWithStaticInitializationAndInstanceOverlayMethod()
+            .getObject());
     assertEquals(7, NativeJsTypeInterfaceWithComplexStaticInitialization.object);
   }
 }