Fix ICE due to abstract supertype method and default method conflict.

Change-Id: I679470829cc568108f23d97d10b18f00d64fc4e3
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ComputeOverridesAndImplementDefaultMethods.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ComputeOverridesAndImplementDefaultMethods.java
index 0fd1d28..d31a06b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ComputeOverridesAndImplementDefaultMethods.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ComputeOverridesAndImplementDefaultMethods.java
@@ -138,15 +138,14 @@
     // Compute interface overrides, fix accidental overrides and implement default methods.
     for (String signature : interfaceMethodsBySignature.keySet()) {
       Collection<JMethod> interfaceMethods = interfaceMethodsBySignature.get(signature);
-      JMethod baseImplementingMethod = polymorphicMethodsByExtendedSignature.get(signature);
-      if (baseImplementingMethod == null) {
+      JMethod implementingMethod = polymorphicMethodsByExtendedSignature.get(signature);
+      if (implementingMethod == null) {
         // See if there is a package private method whose visibility is made public by the
         // override (can actually only happen for abstract methods, as it is a compiler error
         // otherwise.
-        baseImplementingMethod = polymorphicMethodsByExtendedSignature.get(
+        implementingMethod = polymorphicMethodsByExtendedSignature.get(
             computePackagePrivateSignature(type.getPackageName(), signature));
       }
-      JMethod implementingMethod = baseImplementingMethod;
       if (implementingMethod == null || implementingMethod.getEnclosingType() != type) {
         implementingMethod = maybeAddSyntheticOverride(type, implementingMethod, interfaceMethods);
         if (implementingMethod == null) {
@@ -246,17 +245,17 @@
    * interfaces when is only one declaration of the method in the super interface hierarchy.
    */
   private JMethod maybeAddSyntheticOverride(
-      JDeclaredType type, JMethod method, Collection<JMethod> interfaceMethods) {
+      JDeclaredType type, JMethod superMethod, Collection<JMethod> interfaceMethods) {
 
     // If there is a default implementation it will be first and the only default in the collection
     // (as multiple "active" defaults are a compiler error).
     JMethod interfaceMethod = interfaceMethods.iterator().next();
     assert !interfaceMethod.isStatic();
 
-    JMethod implementingMethod = method;
+    JMethod implementingMethod = superMethod;
 
     // Only populate classes with stubs, forwarding methods or default implementations.
-    if (needsStubMethod(type, method, interfaceMethod)) {
+    if (needsDefaultImplementationStubMethod(type, superMethod, interfaceMethod)) {
 
       assert FluentIterable.from(interfaceMethods).filter(new Predicate<JMethod>() {
         @Override
@@ -277,25 +276,25 @@
       implementingMethod = JjsUtils.createForwardingMethod(type, interfaceMethod);
 
       defaultMethodsByForwardingMethod.put(implementingMethod, interfaceMethod);
-    } else if (method == null && interfaceMethod.isAbstract() &&
+    } else if (superMethod == null && interfaceMethod.isAbstract() &&
         (type instanceof JClassType || interfaceMethods.size() > 1)) {
       // It is an abstract stub
       implementingMethod = JjsUtils.createSyntheticAbstractStub(type, interfaceMethod);
-    } else if (type instanceof JClassType && method.getEnclosingType() != type &&
+    } else if (type instanceof JClassType && superMethod.getEnclosingType() != type &&
         !FluentIterable.from(interfaceMethods)
-            .allMatch(Predicates.in(method.getOverriddenMethods()))) {
+            .allMatch(Predicates.in(superMethod.getOverriddenMethods()))) {
         // the implementing method does not override all interface declared methods with the same
         // signature.
-      if (method.isAbstract()) {
+      if (superMethod.isAbstract()) {
         implementingMethod = JjsUtils.createSyntheticAbstractStub(type, interfaceMethod);
       } else {
         // Creates a forwarding method to act as the place holder for this accidental override.
-        implementingMethod = JjsUtils.createForwardingMethod(type, method);
+        implementingMethod = JjsUtils.createForwardingMethod(type, superMethod);
         implementingMethod.setSyntheticAccidentalOverride();
 
-        if (method.isFinal()) {
+        if (superMethod.isFinal()) {
           // To keep consistency we reset the final mark
-          method.setFinal(false);
+          superMethod.setFinal(false);
         }
       }
     }
@@ -304,23 +303,34 @@
       polymorphicMethodsByExtendedSignatureByType.get(type)
           .put(implementingMethod.getSignature(), implementingMethod);
 
-      if (method != null && method != implementingMethod) {
-        addOverridingMethod(method, implementingMethod);
+      if (superMethod != null && superMethod != implementingMethod) {
+        addOverridingMethod(superMethod, implementingMethod);
       }
     }
     return implementingMethod;
   }
 
   /**
-   * Return true if the type {@code type} need to replace {@code method} (possibly {@code null})
-   * with a (forwarding) stub due to {@code interfaceMethod}?
+   * Return true if the type {@code type} need to replace {@code superMethod} (possibly {@code null})
+   * with a (forwarding) stub due to default {@code interfaceMethod}.
    */
-  private boolean needsStubMethod(JDeclaredType type, JMethod method, JMethod interfaceMethod) {
-    return type instanceof JClassType &&
-        interfaceMethod.isDefaultMethod() && (method == null ||
-        method.isDefaultMethod() &&
-            defaultMethodsByForwardingMethod.keySet().contains(method) &&
-            defaultMethodsByForwardingMethod.get(method) != interfaceMethod);
+  private boolean needsDefaultImplementationStubMethod(
+      JDeclaredType type, JMethod superMethod, JMethod interfaceMethod) {
+    if (!interfaceMethod.isDefaultMethod() || type instanceof JInterfaceType) {
+      // Only implement default methods in classes.
+      return false;
+    }
+    if (superMethod == null || (superMethod.isAbstract() && superMethod.isSynthetic())) {
+      // The interface method is not implemented or an abstract stub was synthesized as the super
+      // (not necessarily direct) implementation.
+      return true;
+    }
+    JMethod superForwardingMethod = defaultMethodsByForwardingMethod.get(superMethod);
+    // A default superMethod stub is in place in the supertype, and needs to be replaced if it does
+    // not forward to the required default implementation.
+    return superForwardingMethod != null
+        && superForwardingMethod.isDefaultMethod()
+        && superForwardingMethod != interfaceMethod;
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
index b9467cb4..ee98dfa 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
@@ -198,11 +198,6 @@
     JMethod forwardingMethod = createEmptyMethodFromExample(type, methodToDelegateTo, false);
     forwardingMethod.setForwarding();
 
-    // This is a synthetic forwading method due to a default.
-    if (methodToDelegateTo.isDefaultMethod()) {
-      forwardingMethod.setDefaultMethod();
-    }
-
     if (methodToDelegateTo.isJsOverlay() && type.isJsNative()) {
       forwardingMethod.isJsOverlay();
     }
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 87e1e1a..aaf4248 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
@@ -1123,6 +1123,46 @@
     assertEquals("IRight.m()", new B().m());
   }
 
+  static class DefaultTrumpsOverSyntheticAbstractStub {
+    interface SuperInterface {
+      String m();
+    }
+
+    interface SubInterface extends SuperInterface {
+      default String m() {
+        return "SubInterface.m()";
+      }
+    }
+  }
+
+  public void testMultipleDefaults_defaultShadowsOverSyntheticAbstractStub() {
+    abstract class A implements DefaultTrumpsOverSyntheticAbstractStub.SuperInterface { }
+    class B extends A implements DefaultTrumpsOverSyntheticAbstractStub.SubInterface { }
+
+    assertEquals("SubInterface.m()", new B().m());
+  }
+
+  static class DefaultTrumpsOverDefaultOnSuperAbstract {
+    interface SuperInterface {
+      default String m() {
+        return "SuperInterface.m()";
+      }
+    }
+
+    interface SubInterface extends SuperInterface {
+      default String m() {
+        return "SubInterface.m()";
+      }
+    }
+  }
+
+  public void testMultipleDefaults_defaultShadowsOverDefaultOnSuperAbstract() {
+    abstract class A implements DefaultTrumpsOverDefaultOnSuperAbstract.SuperInterface { }
+    class B extends A implements DefaultTrumpsOverDefaultOnSuperAbstract.SubInterface { }
+
+    assertEquals("SubInterface.m()", new B().m());
+  }
+
   interface InterfaceWithThisReference {
     default String n() {
       return "default n";
diff --git a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
index 4370746..6525b0a 100644
--- a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -252,6 +252,14 @@
     assertFalse(isGwtSourceLevel8());
   }
 
+  public void testMultipleDefaults_defaultShadowsOverSyntheticAbstractStub() {
+    assertFalse(isGwtSourceLevel8());
+  }
+
+  public void testMultipleDefaults_defaultShadowsOverDefaultOnSuperAbstract() {
+    assertFalse(isGwtSourceLevel8());
+  }
+
   public void testInterfaceThis() {
     assertFalse(isGwtSourceLevel8());
   }
@@ -276,7 +284,7 @@
     assertFalse(isGwtSourceLevel8());
   }
 
-    private boolean isGwtSourceLevel8() {
+  private boolean isGwtSourceLevel8() {
     return JUnitShell.getCompilerOptions().getSourceLevel().compareTo(SourceLevel.JAVA8) >= 0;
   }
 }
\ No newline at end of file