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() {
}