Fix qualified super reference dispatch.

Bug: #9614
Bug-Link: https://github.com/gwtproject/gwt/issues/9614
Change-Id: I54bf51dd32744b12d3d8e279eaebaf284560700c
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index 07178c7..656fc3a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -1553,11 +1553,8 @@
 
         JMethodCall methodCall = new JMethodCall(info, receiver, method);
 
-        // On a super ref, don't allow polymorphic dispatch. Oddly enough,
-        // QualifiedSuperReference not derived from SuperReference!
-        boolean isSuperRef =
-            x.receiver instanceof SuperReference || x.receiver instanceof QualifiedSuperReference;
-        if (isSuperRef) {
+        // On a super ref, don't allow polymorphic dispatch.
+        if (isSuperReference(x.receiver)) {
           methodCall.setStaticDispatchOnly();
         }
 
@@ -1885,7 +1882,7 @@
         // For static methods, instance will be null
         samCall = new JMethodCall(info, instance, referredMethod);
         // if super::method, we need static dispatch
-        if (x.lhs instanceof SuperReference) {
+        if (isSuperReference(x.lhs)) {
           samCall.setStaticDispatchOnly();
         }
       }
@@ -4471,6 +4468,13 @@
     }
   }
 
+  /**
+   * Returns <code>true</code> if the expression is either unqualified or qualified super reference.
+   */
+  private static boolean isSuperReference(Expression expression) {
+    return expression instanceof SuperReference || expression instanceof QualifiedSuperReference;
+  }
+
   static class JdtPrivateHacks {
     /**
      * Reflective access to {@link ForeachStatement#collectionElementType}.
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java
index fd3f5df..62f8b83 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java
@@ -19,11 +19,14 @@
 import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConstructor;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.thirdparty.guava.common.base.Joiner;
 
 import java.util.Collections;
@@ -1208,6 +1211,76 @@
     compileSnippetToJS(entryPointClass);
   }
 
+  public void testSuperReferenceExpression() throws Exception {
+    addAll(JavaResourceBase.createMockJavaResource("test.I",
+        "package test;",
+        "interface I {",
+        "  int get();",
+        "}"
+    ));
+    addAll(JavaResourceBase.createMockJavaResource("test.Y",
+        "package test;",
+        "class Y {",
+        "  int foo(){",
+        "    return 42;",
+        "  }",
+        "}"
+    ));
+    addAll(JavaResourceBase.createMockJavaResource("test.X",
+        "package test;",
+        "class X extends Y {",
+        "  int foo(){",
+        "    I i = super::foo;",
+        "    return i.get();",
+        "  }",
+        "}"
+    ));
+
+    JProgram program = compileSnippet("void", "new X().foo();");
+
+    JDeclaredType methodReferenceType = (JDeclaredType) findType(program, "X$0methodref$foo$Type");
+    JField outerField = methodReferenceType.getFields().get(0);
+    JMethod referenceMethod = findMethod(methodReferenceType, "get");
+    JMethodBody methodBody = (JMethodBody) referenceMethod.getBody();
+    JReturnStatement returnStatement = (JReturnStatement) methodBody.getStatements().get(0);
+    assertEquals("this." + outerField.getName() + ".Y.foo()", returnStatement.getExpr().toSource());
+  }
+
+  public void testQualifiedSuperReferenceExpression() throws Exception {
+    addAll(JavaResourceBase.createMockJavaResource("test.I",
+        "package test;",
+        "interface I {",
+        "  int get();",
+        "}"
+    ));
+    addAll(JavaResourceBase.createMockJavaResource("test.Y",
+        "package test;",
+        "class Y {",
+        "  int foo(){",
+        "    return 42;",
+        "  }",
+        "}"
+    ));
+    addAll(JavaResourceBase.createMockJavaResource("test.X",
+        "package test;",
+        "class X extends Y {",
+        "  int foo(){",
+        "    I i = X.super::foo;",
+        "    return i.get();",
+        "  }",
+        "}"
+    ));
+
+    JProgram program = compileSnippet("void", "new X().foo();");
+
+    JDeclaredType methodReferenceType = (JDeclaredType) findType(program, "X$0methodref$foo$Type");
+    JField outerField = methodReferenceType.getFields().get(0);
+    JMethod referenceMethod = findMethod(methodReferenceType, "get");
+    JMethodBody methodBody = (JMethodBody) referenceMethod.getBody();
+    JReturnStatement returnStatement = (JReturnStatement) methodBody.getStatements().get(0);
+    assertEquals("this." + outerField.getName() + ".Y.foo()", returnStatement.getExpr().toSource());
+  }
+
   @Override
   protected void optimizeJava() {
   }
diff --git a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
index 63b6e6c..907d9f3 100644
--- a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -371,6 +371,27 @@
     assertEquals(42, new X().goo());
   }
 
+  public void testQualifiedSuperReferenceExpression() {
+    class Y {
+      int foo(Integer i) {
+        return 42;
+      }
+    }
+
+    class X extends Y {
+      int foo(Integer i) {
+        return 23;
+      }
+
+      int goo() {
+        I i = X.super::foo;
+        return i.foo(0);
+      }
+    }
+
+    assertEquals(42, new X().goo());
+  }
+
   static class X2 {
     protected int field;
     void foo() {
diff --git a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
index 1c20945..312d70c 100644
--- a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -112,6 +112,10 @@
     assertFalse(isGwtSourceLevel9());
   }
 
+  public void testQualifiedSuperReferenceExpression() {
+    assertFalse(isGwtSourceLevel9());
+  }
+
   public void testSuperReferenceExpressionWithVarArgs() {
     assertFalse(isGwtSourceLevel9());
   }