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