Fix bad boxing in (instance) method references.

Bug: #9340
Bug-Link: http://github.com/gwtproject/gwt/issues/9340
Change-Id: I4305181d680044bf8892d6acd0482df1f2e2eccb
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 cb066da..4044774 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
@@ -1867,17 +1867,22 @@
           JExpression paramExpr = param.makeRef(info);
           // params may need to be boxed or unboxed
           TypeBinding destParam = null;
+          // The method declared in the functional interface might have more parameters than the
+          // method referred by the method reference. In the case of an instance method without
+          // an explicit qualifier (A::m vs instance::m) the method in the functional interface will
+          // have an additional parameter for the instance preceding all the method parameters.
+          TypeBinding samParameterBinding =
+              samBinding.parameters[paramNumber
+                  + (samBinding.parameters.length - referredMethodBinding.parameters.length)];
           // if it is not the trailing param or varargs, or interface method is already varargs
           if (varArgInitializers == null || !referredMethodBinding.isVarargs() || (paramNumber < varArg)) {
             destParam = referredMethodBinding.parameters[paramNumber];
-            paramExpr = boxOrUnboxExpression(paramExpr, samBinding.parameters[paramNumber],
-                destParam);
+            paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam);
             samCall.addArg(paramExpr);
-          } else if (!samBinding.parameters[paramNumber].isArrayType()) {
+          } else if (!samParameterBinding.isArrayType()) {
             // else add trailing parameters to var-args initializer list for an array
             destParam = referredMethodBinding.parameters[varArg].leafComponentType();
-            paramExpr = boxOrUnboxExpression(paramExpr, samBinding.parameters[paramNumber],
-                destParam);
+            paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam);
             varArgInitializers.add(paramExpr);
           }
           paramNumber++;
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 aaf4248..45f38de 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
@@ -1441,19 +1441,19 @@
     V apply(T t, U u);
   }
 
-  public void testMethodReferenceImplementedInSuperclass() {
+  public void testMethodReference_implementedInSuperclass() {
     MyFunction1<StringBuilder, String> toString = StringBuilder::toString;
     assertEquals("Hello", toString.apply(new StringBuilder("Hello")));
   }
 
   static MyFunction2<String, String, String> concat = (s,t) -> s + t;
 
-  public void testMethodReferenceWithGenericTypeParameters() {
-    testMethodReferencesWithGenericTypeParameters(
+  public void testMethodReference_genericTypeParameters() {
+    testMethodReference_genericTypeParameters(
         new Some<String>("Hell", concat), "Hell", "o", concat);
   }
 
-  private static <T> void testMethodReferencesWithGenericTypeParameters(
+  private static <T> void testMethodReference_genericTypeParameters(
       Some<T> some, T t1, T t2, MyFunction2<T, T, T> combine) {
     T t1t2 = combine.apply(t1, t2);
 
@@ -1471,4 +1471,87 @@
     assertEquals(t1t2,
         ((MyFunction2<T, MyFunction2<T, T, T>, Some<T>>) Some<T>::new).apply(t1t2, combine).m1());
   }
+
+  static MyFunction2<Integer, Integer, Integer> addInteger = (s,t) -> s + t;
+
+  @FunctionalInterface
+  interface MyIntFunction1 {
+    int apply(int t);
+  }
+
+  @FunctionalInterface
+  interface MyIntFunction2 {
+    int apply(int t, int u);
+  }
+
+  @FunctionalInterface
+  interface MyIntFuncToSomeIntegeFunction2 {
+    SomeInteger apply(int t, MyFunction2<Integer, Integer, Integer> u);
+  }
+
+  @FunctionalInterface
+  interface MySomeIntegerFunction1 {
+    int apply(SomeInteger t);
+  }
+
+  @FunctionalInterface
+  interface MySomeIntegerIntFunction2 {
+    int apply(SomeInteger t, int u);
+  }
+
+  static MyIntFunction2 addint = (s,t) -> s + t;
+
+  static class SomeInteger {
+    int s;
+    MyFunction2<Integer, Integer ,Integer> combine;
+    SomeInteger(int s, MyFunction2<Integer, Integer, Integer>  combine) {
+      this.s = s;
+      this.combine = combine;
+    }
+    public int m(int s2) {
+      return combine.apply(s, s2);
+    }
+    public int m1() {
+      return s;
+    }
+  }
+
+  public void testMethodReference_autoboxing() {
+    SomeInteger some = new SomeInteger(3, addInteger);
+
+    // Test all 4 flavours of methodReference autoboxing parameters.
+    // 1. Static method
+    assertEquals((Integer) 5, ((MyFunction1<Integer, Integer>) Java8Test::m).apply(5));
+    // 2. Qualified instance method
+    assertEquals((Integer) 5, ((MyFunction1<Integer, Integer>) some::m).apply(2));
+    // 3. Unqualified instance method
+    assertEquals((Integer) 3, ((MyFunction1<SomeInteger, Integer>) SomeInteger::m1).apply(some));
+    assertEquals((Integer) 5, ((MyFunction2<SomeInteger, Integer, Integer>)
+        SomeInteger::m).apply(some, 2));
+    assertEquals((Integer) 5,
+        ((MyFunction1<SomeInteger, Integer>)
+            SomeInteger::m1).apply(new SomeInteger(5, addInteger)));
+    // 4. Constructor reference.
+    assertEquals(5,
+        ((MyFunction2<Integer, MyFunction2<Integer, Integer, Integer>, SomeInteger>)
+            SomeInteger::new).apply(5, addInteger).m1());
+
+    // Test all 4 flavours of methodReference (interface unboxed)
+    // 1. Static method
+    assertEquals(5, ((MyIntFunction1) Java8Test::m).apply(5));
+    // 2. Qualified instance method
+    assertEquals(5, ((MyIntFunction1) some::m).apply(2));
+    // 3. Unqualified instance method
+    assertEquals(3, ((MySomeIntegerFunction1) SomeInteger::m1).apply(some));
+    // The next expression was the one that triggered bug #9346 where decisions on whether to
+    // box/unbox were decided incorrectly due to differring number of parameters in the method
+    // reference and the functional interface method.
+    assertEquals(5, ((MySomeIntegerIntFunction2) SomeInteger::m).apply(some, 2));
+    assertEquals(5,
+        ((MySomeIntegerFunction1)
+            SomeInteger::m1).apply(new SomeInteger(5, addInteger)));
+    // 4. Constructor reference.
+    assertEquals(5,
+        ((MyIntFuncToSomeIntegeFunction2) SomeInteger::new).apply(5, addInteger).m1());
+  }
 }
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 6525b0a..3c360f1 100644
--- a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -276,11 +276,15 @@
     assertFalse(isGwtSourceLevel8());
   }
 
-  public void testMethodReferenceImplementedInSuperclass() {
+  public void testMethodReference_implementedInSuperclass() {
     assertFalse(isGwtSourceLevel8());
   }
 
-  public void testMethodReferenceWithGenericTypeParameters() {
+  public void testMethodReference_genericTypeParameters() {
+    assertFalse(isGwtSourceLevel8());
+  }
+
+  public void testMethodReference_autoboxing() {
     assertFalse(isGwtSourceLevel8());
   }