Fix for bad method resolution in GwtAstBuilder.
Bug: #9598
Bug-Link: https://github.com/gwtproject/gwt/issues/9598
Change-Id: Ie6a5b485eb524436d9f5fe4f7dc758d35fcc4d60
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 fce0237..471c7c2 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
@@ -2917,7 +2917,6 @@
} else {
body.getBlock().addStmt(call.makeReturnStatement());
}
- typeMap.setMethod(sourceMethodBinding, bridgeMethod);
return bridgeMethod;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java
index 1a9c4a3..77b5363 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java
@@ -33,6 +33,7 @@
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.util.StringInterner;
+import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import com.google.gwt.thirdparty.guava.common.collect.Interner;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
@@ -184,13 +185,26 @@
}
public void setField(FieldBinding binding, JField field) {
+ // Make sure that the enclosing type of the field binding corresponds to the enclosing type of
+ // field to avoid associating a field binding with the wrong JField.
+ Preconditions.checkArgument(get(binding.declaringClass.erasure()) == field.getEnclosingType());
+
String key = JdtUtil.signature(binding);
- sourceFields.put(key, field);
+ JField oldField = sourceFields.put(key, field);
+
+ // Make sure this is called at most once per field binding.
+ Preconditions.checkState(oldField == null);
}
public void setMethod(MethodBinding binding, JMethod method) {
+ // Make sure that the enclosing type of the method binding corresponds to the enclosing type of
+ // method to avoid associating a method binding with the wrong JMethod.
+ Preconditions.checkArgument(get(binding.declaringClass.erasure()) == method.getEnclosingType());
String key = JdtUtil.signature(binding);
- sourceMethods.put(key, method);
+ JMethod oldMethod = sourceMethods.put(key, method);
+
+ // Make sure this is called at most once per method binding.
+ Preconditions.checkState(oldMethod == null);
}
public void setSourceType(SourceTypeBinding binding, JDeclaredType type) {
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/GwtAstBuilderTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/GwtAstBuilderTest.java
index c4837a2..d332443 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/GwtAstBuilderTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/GwtAstBuilderTest.java
@@ -46,6 +46,7 @@
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -276,6 +277,40 @@
}
}
+ public void testBridgeMethodResolution() throws UnableToCompleteException {
+ sourceLevel = SourceLevel.JAVA9;
+
+ sources.add(JavaResourceBase.createMockJavaResource("test.SuperInterface",
+ "package test;",
+ "public interface SuperInterface<T> {",
+ " void m(T t);",
+ "}",
+ "interface SubInterface extends SuperInterface<String> {",
+ " void m(String t);",
+ " default SubInterface get() { ",
+ " // A lambda that will have a bridge m(Object) -> m(String)",
+ " return o -> {};",
+ " } ",
+ " static <T> void applyM(SuperInterface<T> s) { s.m(null); } ",
+ "}"
+ ));
+
+ JProgram program = compileProgram("test.SuperInterface");
+ JMethod applyM = findQualifiedMethod(program, "test.SubInterface.applyM");
+
+ final Set<String> calledMethods = new HashSet<>();
+
+ new JVisitor() {
+ @Override
+ public void endVisit(JMethodCall x, Context ctx) {
+ calledMethods.add(x.getTarget().getQualifiedName());
+ }
+ }.accept(applyM);
+ assertEquals(
+ Sets.newHashSet(Arrays.asList("test.SuperInterface.m(Ljava/lang/Object;)V")),
+ calledMethods);
+ }
+
public void testUniqueArrayTypeInstance() throws UnableToCompleteException {
JProgram program = compileProgram("test.DalGrid");
Set<String> arrayTypeNames = Sets.newHashSet();
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 d46bbdb..63b6e6c 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
@@ -27,6 +27,7 @@
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.IntFunction;
+import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -2038,4 +2039,24 @@
};
assertEquals("hello", helloSupplier.get());
}
+
+ interface Selector extends Predicate<String> {
+ @Override
+ boolean test(String object);
+
+ default Selector trueSelector() {
+ // Unused variable that creates a lambda with a bridge for the method test. The bug #9598
+ // was caused by GwtAstBuilder associating the bridge method Lambda.test(Object) on the
+ // lambda below to the method Predicate.test(Object), causing the method resolution in the
+ // code that refers to the Predicate.test(Object) in the test below to refer to
+ // Lambda.test(Object) which is the wrong method.
+ return receiver -> true;
+ }
+ }
+
+ // Regression tests for #9598
+ public void testImproperMethodResolution() {
+ Predicate p = o -> true;
+ assertTrue(p.test(null));
+ }
}
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 e706f45..1c20945 100644
--- a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -352,6 +352,10 @@
assertFalse(isGwtSourceLevel9());
}
+ public void testImproperMethodResolution() {
+ assertFalse(isGwtSourceLevel9());
+ }
+
private boolean isGwtSourceLevel9() {
return JUnitShell.getCompilerOptions().getSourceLevel().compareTo(SourceLevel.JAVA9) >= 0;
}