Fix incorrect pruning of overridden JsMethods.
JsMember information is inherited from overriden methods
and pruning any of these overridden methods might cause the
compiler to "forget" that a JsMethod/JsProperty is a
JsMethod/JsProperty.
This is the correct behaviour in most cases (as only if
-nogenerateJsInteropExports those overriden members are pruned),
except for the case of native JsMethods.
This patch makes methods and fields that can be implemented
externally live.
Bug: #9358
Bug-Link: http://github.com/gwtproject/gwt/issues/9358
Change-Id: I80637883849af5d99542f8d0bc35c3e5b895a9e1
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
index e4cf46a..1299734 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -577,11 +577,10 @@
}
/**
- * Subclasses of JavaScriptObject are never instantiated directly. They are
- * implicitly created when a JSNI method passes a reference to an existing
- * JS object into Java code. If any point in the program can pass a value
- * from JS into Java which could potentially be cast to JavaScriptObject, we
- * must rescue JavaScriptObject.
+ * Subclasses of JavaScriptObject are never instantiated directly. They are implicitly created
+ * when a JSNI method passes a reference to an existing JS object into Java code. If any point
+ * in the program can pass a value from JS into Java which could potentially be cast to
+ * JavaScriptObject, we must rescue JavaScriptObject.
*
* @param type The type of the value passing from Java to JavaScript.
* @see com.google.gwt.core.client.JavaScriptObject
@@ -760,12 +759,11 @@
}
/**
- * The code is very tightly tied to the behavior of
- * Pruner.CleanupRefsVisitor. CleanUpRefsVisitor will prune unread
- * parameters, and also prune any matching arguments that don't have side
- * effects. We want to make control flow congruent to pruning, to avoid the
- * need to iterate over Pruner until reaching a stable point, so we avoid
- * actually rescuing such arguments until/unless the parameter is read.
+ * The code is very tightly tied to the behavior of Pruner.CleanupRefsVisitor.
+ * CleanUpRefsVisitor will prune unread parameters, and also prune any matching arguments that
+ * don't have side effects. We want to make control flow congruent to pruning, to avoid the need
+ * to iterate over Pruner until reaching a stable point, so we avoid actually rescuing such
+ * arguments until/unless the parameter is read.
*/
private void rescueArgumentsIfParametersCanBeRead(JMethodCall call) {
JMethod method = call.getTarget();
@@ -847,15 +845,14 @@
}
for (JField field : type.getFields()) {
if (!field.isStatic() && membersToRescueIfTypeIsInstantiated.contains(field)) {
- rescue(field);
+ rescue(field);
}
}
}
/**
- * Assume that <code>method</code> is live. Rescue any overriding methods
- * that might be called if <code>method</code> is called through virtual
- * dispatch.
+ * Assume that <code>method</code> is live. Rescue any overriding methods that might be called
+ * if <code>method</code> is called through virtual dispatch.
*/
private void rescueOverridingMethods(JMethod method) {
if (method.isStatic()) {
@@ -1028,14 +1025,14 @@
// first time through, record all exported methods
for (JMethod method : type.getMethods()) {
- if (method.isJsInteropEntryPoint()) {
+ if (method.isJsInteropEntryPoint() || method.canBeImplementedExternally()) {
// treat class as instantiated, since a ctor may be called from JS export
rescuer.rescue(method.getEnclosingType(), true);
traverseFrom(method);
}
}
for (JField field : type.getFields()) {
- if (field.isJsInteropEntryPoint()) {
+ if (field.isJsInteropEntryPoint() || field.canBeImplementedExternally()) {
rescuer.rescue(field.getEnclosingType(), true);
rescuer.rescue(field);
}
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 27f6879..7415529 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
@@ -526,8 +526,12 @@
for (JParameter param : exampleMethod.getParams()) {
emptyMethod.cloneParameter(param);
}
- JMethodBody body = new JMethodBody(exampleMethod.getSourceInfo());
- emptyMethod.setBody(body);
+ // If the enclosing type is native, make sure the synthetic empty method is native by leaving
+ // the body = null.
+ if (!inType.isJsNative()) {
+ JMethodBody body = new JMethodBody(exampleMethod.getSourceInfo());
+ emptyMethod.setBody(body);
+ }
emptyMethod.freezeParamTypes();
inType.addMethod(emptyMethod);
return emptyMethod;
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
index 0fdbbc3..26ca07c 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
@@ -35,6 +35,8 @@
import java.util.Map;
import javaemul.internal.annotations.DoNotInline;
+import jsinterop.annotations.JsPackage;
+import jsinterop.annotations.JsProperty;
import jsinterop.annotations.JsType;
/**
@@ -343,4 +345,28 @@
assertEquals("Array mismatch at element " + i , expected[i], actual[i]);
}
}
+
+ @JsType(isNative = true)
+ interface SomeNativeInterface {
+ @JsProperty
+ String getTextContent();
+ }
+
+ @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
+ static abstract class AbstractNativeType implements SomeNativeInterface {
+ }
+
+ private static native AbstractNativeType createAbstractNativeType(String value) /*-{
+ return {textContent : value};
+ }-*/;
+
+ // Tests that methods that override native JsMethods are generated properly w.r.t
+ // JsMethod/JsProperty annotations even if the overridden method is not live.
+ // See bug #9358
+ //
+ // Make sure this method is part of a suite that is run with -nogenerateJsInteropExports.
+ public void testNativeJsMethodDispatch_unreferencedSupertypeMethod() {
+ final AbstractNativeType o = createAbstractNativeType("Hello");
+ assertEquals("Hello", o.getTextContent());
+ }
}