Adhere to Java 8 semantics in static initialization for interfaces.

Change-Id: Ibdc432f0ea7acb894a0616a3764ec7e65b297b5d
Bug-Link: https://github.com/gwtproject/gwt/issues/9090
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
index 1ce0ab3..0f52fa3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JInterfaceType.java
@@ -17,6 +17,8 @@
 
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.SourceOrigin;
+import com.google.gwt.thirdparty.guava.common.base.Predicate;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
 
 import java.io.Serializable;
 
@@ -74,6 +76,15 @@
     return false;
   }
 
+  public boolean hasDefaultMethods() {
+    return Iterables.any(getMethods(), new Predicate<JMethod>() {
+      @Override
+      public boolean apply(JMethod method) {
+        return method.isDefaultMethod();
+      }
+    });
+  }
+
   @Override
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index 7ff2a6c..cde69dd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -2523,16 +2523,10 @@
 
     protected void endVisit(TypeDeclaration x) {
       JDeclaredType type = curClass.type;
-      /*
-       * Make clinits chain to super class (JDT doesn't write code to do this).
-       * Call super class $clinit;
-       */
-      if (type.getSuperClass() != null) {
-        JMethod myClinit = type.getClinitMethod();
-        JMethod superClinit = type.getSuperClass().getClinitMethod();
-        JMethodCall superClinitCall = new JMethodCall(myClinit.getSourceInfo(), null, superClinit);
-        JMethodBody body = (JMethodBody) myClinit.getBody();
-        body.getBlock().addStmt(0, superClinitCall.makeStatement());
+
+      // Synthesize super clinit calls.
+      if (type instanceof JClassType) {
+        JjsUtils.synthesizeStaticInitializerChain(type);
       }
 
       // Implement getClass() implementation for all non-Object classes.
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 73fbfea..2b6862e 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
@@ -434,6 +434,28 @@
     return translatorByLiteralClass.get(literal.getClass()).translate(literal);
   }
 
+  static void synthesizeStaticInitializerChain(JDeclaredType type) {
+    // Implement static initialization as described in (Java 8) JLS 12.4.2.
+    List<JStatement> superClinitCalls = Lists.newArrayList();
+    SourceInfo sourceInfo = type.getSourceInfo();
+
+    // First call the static initializer for the superclass.
+    JClassType superClass = type.getSuperClass();
+    if (superClass != null) {
+      superClinitCalls.add(
+          new JMethodCall(sourceInfo, null, superClass.getClinitMethod()).makeStatement());
+    }
+
+    // Recurse over interfaces in preorder initializing the ones that have default methods.
+    for (JInterfaceType interfaceType : getSuperInterfacesRequiringInitialization(type)) {
+      superClinitCalls.add(
+          new JMethodCall(sourceInfo, null, interfaceType.getClinitMethod()).makeStatement());
+    }
+
+    JMethodBody body = (JMethodBody) type.getClinitMethod().getBody();
+    body.getBlock().getStatements().addAll(0, superClinitCalls);
+  }
+
   private static Map<Class<? extends JLiteral>, LiteralTranslators> translatorByLiteralClass =
       new ImmutableMap.Builder<Class<? extends JLiteral>, LiteralTranslators>()
           .put(JBooleanLiteral.class, LiteralTranslators.BOOLEAN_LITERAL_TRANSLATOR)
@@ -551,6 +573,19 @@
     return emptyMethod;
   }
 
+  private static Iterable<JInterfaceType> getSuperInterfacesRequiringInitialization(
+      JDeclaredType type) {
+    Iterable<JInterfaceType> interfaces = Collections.emptyList();
+    for (JInterfaceType interfaceType : type.getImplements()) {
+      interfaces =
+          Iterables.concat(interfaces, getSuperInterfacesRequiringInitialization(interfaceType));
+      if (interfaceType.hasDefaultMethods()) {
+        interfaces = Iterables.concat(interfaces, Collections.singleton(interfaceType));
+      }
+    }
+    return interfaces;
+  }
+
   private JjsUtils() {
   }
 }
\ No newline at end of file
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 5f51cea..50afc72 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
@@ -18,6 +18,9 @@
 import com.google.gwt.core.client.GwtScriptOnly;
 import com.google.gwt.junit.client.GWTTestCase;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 /**
  * Tests Java 8 features. It is super sourced so that gwt can be compiles under Java 7.
  *
@@ -1192,4 +1195,67 @@
     assertEquals("Object super n", new A().callNWithSuper());
     assertEquals("Object default n", new A().callNWithInterfaceSuper());
   }
+
+  private static List<String> initializationOrder;
+
+  private static int get(String s) {
+    initializationOrder.add(s);
+    return 1;
+  }
+
+  interface A1 {
+    int fa1 = get("A1");
+
+    default void a1() { }
+  }
+
+  interface A2 {
+    int fa2 = get("A2");
+
+    default void a2() { }
+  }
+
+  interface A3 {
+    int fa3 = get("A3");
+
+    default void a3() { }
+  }
+
+  interface B1 extends A1 {
+    int fb1 = get("B1");
+
+    default void b1() { }
+  }
+
+  interface B2 extends A2 {
+    int fb2 = get("B2");
+
+    default void b2() { }
+  }
+
+  interface B3 extends A3 {
+    int fb3 = get("B3");
+  }
+
+  static class C implements B1, A2  {
+    static {
+      get("C");
+    }
+  }
+
+  static class D extends C implements B2, B3 {
+    static {
+      get("D");
+    }
+  }
+
+  public void testInterfaceWithDefaultMethodsInitialization() {
+    initializationOrder = new ArrayList<String>();
+    new D();
+    assertContentsInOrder(initializationOrder, "A1", "B1", "A2", "C", "B2", "A3", "D");
+  }
+
+  private void assertContentsInOrder(Iterable<String> contents, String... elements) {
+    assertEquals(Arrays.asList(elements).toString(), contents.toString());
+  }
 }
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 e2c8084..894370c 100644
--- a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -116,6 +116,9 @@
   public void testDefaultInterfaceMethodVirtualUpRef() {
   }
 
+  public void testInterfaceWithDefaultMethodsInitialization() {
+  }
+
   public void testDefaultInterfaceMethodMultiple() {
   }