Don't emit unnecessary synthetic overrides.
Change-Id: I43fab8f002de32efbf63d329f792461fc787cba6
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 53c40d7..f61d0b1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -91,6 +91,7 @@
private boolean preventDevirtualization = false;
private boolean hasSideEffects = true;
private boolean defaultMethod = false;
+ private boolean syntheticAccidentalOverride = false;
@Override
public void setJsMemberInfo(String namespace, String name, boolean exported) {
@@ -244,6 +245,14 @@
return enclosingType != null && enclosingType.isJsNative();
}
+ public void setSyntheticAccidentalOverride() {
+ this.syntheticAccidentalOverride = true;
+ }
+
+ public boolean isSyntheticAccidentalOverride() {
+ return syntheticAccidentalOverride;
+ }
+
public void setSpecialization(List<JType> paramTypes, JType returnsType, String targetMethod) {
this.specialization = new Specialization(paramTypes,
returnsType == null ? this.getOriginalReturnType() : returnsType, targetMethod);
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 6fd94c2..0fd1d28 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
@@ -291,6 +291,8 @@
} else {
// Creates a forwarding method to act as the place holder for this accidental override.
implementingMethod = JjsUtils.createForwardingMethod(type, method);
+ implementingMethod.setSyntheticAccidentalOverride();
+
if (method.isFinal()) {
// To keep consistency we reset the final mark
method.setFinal(false);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index 1ce0f44..6b49d6a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -2541,8 +2541,9 @@
*/
private static boolean doesNotHaveConcreteImplementation(JMethod method) {
return method.isAbstract()
- || JProgram.isClinit(method)
- && method.getEnclosingType().getClinitTarget() != method.getEnclosingType();
+ || JjsUtils.isUnnecessarySyntheticAccidentalOverride(method)
+ || (JProgram.isClinit(method)
+ && method.getEnclosingType().getClinitTarget() != method.getEnclosingType());
}
private static class JavaToJsOperatorMap {
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 c9eb44a..003f7fb 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
@@ -69,6 +69,7 @@
import com.google.gwt.thirdparty.guava.common.collect.Collections2;
import com.google.gwt.thirdparty.guava.common.collect.FluentIterable;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import java.util.Arrays;
@@ -320,6 +321,30 @@
}
/**
+ * Returns true if the method is a synthetic accidental override that trivially dispatches to its
+ * same name super.
+ */
+ public static boolean isUnnecessarySyntheticAccidentalOverride(JMethod method) {
+ // Assumptions on synthethic overrides, if any of these change.
+ assert !method.isSyntheticAccidentalOverride() || !method.exposesPackagePrivateMethod();
+
+ if (!method.isSyntheticAccidentalOverride()) {
+ return false;
+ }
+
+ boolean overridesConcreteMethod = Iterables.any(method.getOverriddenMethods(),
+ new Predicate<JMethod>() {
+ @Override
+ public boolean apply(JMethod method) {
+ return !method.isAbstract();
+ }
+ });
+ // A synthetic accidental override is unnecessary iff it retains the same property
+ // name (polyname) as the the concrete method it overrides.
+ return overridesConcreteMethod && !method.exposesNonJsMethod();
+ }
+
+ /**
* Mangles a qualified name into a Javah signature.
*/
public static String javahSignatureFromName(String name) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
index d2461b7..c31d31c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
@@ -374,6 +374,12 @@
private void checkNoStaticJsPropertyCalls() {
new JVisitor() {
@Override
+ public boolean visit(JMethod x, Context ctx) {
+ // Skip unnecessary synthetic override, as they will not be generated.
+ return !JjsUtils.isUnnecessarySyntheticAccidentalOverride(x);
+ }
+
+ @Override
public void endVisit(JMethodCall x, Context ctx) {
JMethod target = x.getTarget();
if (x.isStaticDispatchOnly() && target.isJsPropertyAccessor()) {
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
index a9bb22a..88a8e50 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
@@ -637,8 +637,7 @@
assertBuggySucceeds();
}
- // TODO(rluble): this test should succeed, fix after eliminating the accidental overrides.
- public void testJsPropertyAccidentalSuperCallFails()
+ public void testJsPropertyAccidentalSuperCallSucceeds()
throws UnableToCompleteException {
addSnippetImport("jsinterop.annotations.JsType");
addSnippetImport("jsinterop.annotations.JsProperty");
@@ -653,9 +652,7 @@
"@JsType public static class Buggy extends Super implements Interface {",
"}");
- assertBuggyFails(
- "Cannot call property accessor 'test.EntryPoint$Super.getX()I' via super "
- + "(test/EntryPoint.java:6).");
+ assertBuggySucceeds();
}
public void testMultiplePrivateConstructorsExportSucceeds() throws Exception {
diff --git a/user/test/com/google/gwt/core/client/interop/JsPropertyTest.java b/user/test/com/google/gwt/core/client/interop/JsPropertyTest.java
index ce4dd93..d131d95 100644
--- a/user/test/com/google/gwt/core/client/interop/JsPropertyTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsPropertyTest.java
@@ -365,6 +365,48 @@
}
@JsType(isNative = true)
+ interface AccidentalOverridePropertyJsTypeInterface {
+ @JsProperty
+ int getX();
+ }
+
+ static class AccidentalOverridePropertyBase {
+ public int getX() {
+ return 50;
+ }
+ }
+
+ static class AccidentalOverrideProperty extends AccidentalOverridePropertyBase
+ implements AccidentalOverridePropertyJsTypeInterface {
+ }
+
+ public void testJsPropertyAccidentalOverrideSuperCall() {
+ AccidentalOverrideProperty object = new AccidentalOverrideProperty();
+ assertEquals(50, object.getX());
+ assertEquals(50, getProperty(object, "x"));
+ }
+
+ @JsType
+ static class RemovedAccidentalOverridePropertyBase {
+ @JsProperty
+ public int getX() {
+ return 55;
+ }
+ }
+
+ static class RemovedAccidentalOverrideProperty extends RemovedAccidentalOverridePropertyBase
+ implements AccidentalOverridePropertyJsTypeInterface {
+ }
+
+ public void testJsPropertyRemovedAccidentalOverrideSuperCall() {
+ RemovedAccidentalOverrideProperty object = new RemovedAccidentalOverrideProperty();
+ // If the accidental override here were not removed the access to property x would result in
+ // an infinite loop
+ assertEquals(55, object.getX());
+ assertEquals(55, getProperty(object, "x"));
+ }
+
+ @JsType(isNative = true)
interface JsTypeGetProperty {
@JsProperty