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; };