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()); + } }