Make sure JsFunction calls are not accidentally bound.
Bug: #9328
Bug-Link: http://github.com/gwtproject/gwt/issues/9328
Change-Id: Ice653dbe2f6e423f3ec0af9d450b5d18a88a741d
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 06f1dca..4a24c45 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
@@ -939,7 +939,8 @@
private JsExpression dispatchToJsFunction(JsExpression instance, JMethod method,
List<JsExpression> args, SourceInfo sourceInfo) {
- return createInvocationOrPropertyAccess(InvocationStyle.FUNCTION, sourceInfo, method, instance, null, args);
+ return createInvocationOrPropertyAccess(
+ InvocationStyle.FUNCTION, sourceInfo, method, instance, null, args);
}
private JsExpression dispatchToInstanceMethod(JsExpression instance, JMethod method,
diff --git a/dev/core/src/com/google/gwt/dev/js/JsUtils.java b/dev/core/src/com/google/gwt/dev/js/JsUtils.java
index 2f94c75..6011d45 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsUtils.java
@@ -199,7 +199,7 @@
}
private enum CallStyle {
- DIRECT, USING_CALL_FOR_SUPER, USING_APPLY_FOR_VARARGS_ARRAY
+ DIRECT, USING_CALL, USING_APPLY_FOR_VARARGS_ARRAY
}
private static class InvocationDescriptor {
@@ -230,7 +230,12 @@
JMethod method, JsExpression instance, JsNameRef reference, List<JsExpression> args) {
CallStyle callStyle = invocationStyle == InvocationStyle.SUPER
- ? CallStyle.USING_CALL_FOR_SUPER : CallStyle.DIRECT;
+ // JsFunctions that are accessed through an instance field need to be called using CALL to
+ // avoid accidentally binding "this" to the field's qualifier. See bug #9328.
+ || invocationStyle == InvocationStyle.FUNCTION
+ && instance instanceof JsNameRef
+ && ((JsNameRef) instance).getQualifier() != null
+ ? CallStyle.USING_CALL : CallStyle.DIRECT;
TargetType targetType;
switch (invocationStyle) {
@@ -253,7 +258,8 @@
}
JsExpression lastArgument = Iterables.getLast(args, null);
- boolean needsVarargsApply = method.isJsMethodVarargs() && !(lastArgument instanceof JsArrayLiteral);
+ boolean needsVarargsApply =
+ method.isJsMethodVarargs() && !(lastArgument instanceof JsArrayLiteral);
List<JsExpression> nonVarargArguments = args;
JsExpression varargArgument = null;
if (method.isJsMethodVarargs()) {
@@ -361,9 +367,9 @@
}
}
- public static JsExpression createSuperInvocationOrPropertyAccess(
+ public static JsExpression createCallInvocationOrSuperPropertyAccess(
SourceInfo sourceInfo, InvocationDescriptor invocationDescriptor) {
- assert invocationDescriptor.callStyle == CallStyle.USING_CALL_FOR_SUPER;
+ assert invocationDescriptor.callStyle == CallStyle.USING_CALL;
switch (invocationDescriptor.targetType) {
case SETTER:
assert invocationDescriptor.nonVarargsArguments.size() == 1;
@@ -373,12 +379,14 @@
assert invocationDescriptor.nonVarargsArguments.size() == 0;
// TODO(rluble): implement super getters.
throw new UnsupportedOperationException("Super.getter is unsupported");
+ case FUNCTION:
+ // instance.call(null, p1, ..., pn)
+ return createCallInvocation(sourceInfo, invocationDescriptor.instance,
+ JsNullLiteral.INSTANCE, invocationDescriptor.nonVarargsArguments);
case METHOD:
- // q.name.call(instance, p1, ..., pn)
- return new JsInvocation(sourceInfo,
- createQualifiedNameRef(sourceInfo, invocationDescriptor.reference, "call"),
- Iterables.concat(Collections.singleton(invocationDescriptor.instance),
- invocationDescriptor.nonVarargsArguments));
+ // q.methodname.call(instance, p1, ..., pn)
+ return createCallInvocation(sourceInfo, invocationDescriptor.reference,
+ invocationDescriptor.instance, invocationDescriptor.nonVarargsArguments);
default:
throw new AssertionError("Target type " + invocationDescriptor.targetType
+ " invalid for super invocation");
@@ -386,6 +394,15 @@
}
/**
+ * Synthesize an invocation using .call().
+ */
+ private static JsInvocation createCallInvocation(SourceInfo sourceInfo, JsExpression target,
+ JsExpression instance, Iterable<JsExpression> arguments) {
+ return new JsInvocation(sourceInfo, createQualifiedNameRef(sourceInfo, target, "call"),
+ Iterables.concat(Collections.singleton(instance),arguments));
+ }
+
+ /**
* Invocation styles.
*/
public enum InvocationStyle {
@@ -400,8 +417,8 @@
switch (invocationDescriptor.callStyle) {
case DIRECT:
return createDirectInvocationOrPropertyAccess(sourceInfo, invocationDescriptor);
- case USING_CALL_FOR_SUPER:
- return createSuperInvocationOrPropertyAccess(sourceInfo, invocationDescriptor);
+ case USING_CALL:
+ return createCallInvocationOrSuperPropertyAccess(sourceInfo, invocationDescriptor);
case USING_APPLY_FOR_VARARGS_ARRAY:
return createApplyInvocation(sourceInfo, invocationDescriptor);
}
diff --git a/user/test/com/google/gwt/core/interop/JsFunctionTest.java b/user/test/com/google/gwt/core/interop/JsFunctionTest.java
index 2d0c3dc..b71d71a 100644
--- a/user/test/com/google/gwt/core/interop/JsFunctionTest.java
+++ b/user/test/com/google/gwt/core/interop/JsFunctionTest.java
@@ -15,8 +15,13 @@
*/
package com.google.gwt.core.interop;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
+import jsinterop.annotations.JsFunction;
+import jsinterop.annotations.JsProperty;
+
/**
* Tests JsFunction functionality.
*/
@@ -228,6 +233,29 @@
assertFalse(object instanceof HTMLElementConcreteNativeJsType);
}
+ @JsFunction
+ interface JsFunctionInterface {
+ Object m();
+ }
+
+ private static native JsFunctionInterface createFunctionThatReturnsThis() /*-{
+ return function () { return this; };
+ }-*/;
+
+ // Tests for bug #9328
+ @DoNotRunWith(Platform.HtmlUnitBug)
+ public void testJsFunctionProperty() {
+ class JsFuncionProperty {
+ @JsProperty
+ public JsFunctionInterface func;
+ }
+ JsFuncionProperty jsFuncionProperty = new JsFuncionProperty();
+ jsFuncionProperty.func = createFunctionThatReturnsThis();
+ assertNotSame(jsFuncionProperty, jsFuncionProperty.func.m());
+ JsFunctionInterface funcInVar = jsFuncionProperty.func;
+ assertSame(jsFuncionProperty.func.m(), funcInVar.m());
+ }
+
// uncomment when Java8 is supported.
// public void testJsFunctionLambda_JS() {
// MyJsFunctionInterface jsFunctionInterface = a -> { return a + 2; };