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