Version 1.5.38

Cherry-pick:
  Use the arity of k-style lambda group's interface, not main method.
CL: https://r8-review.googlesource.com/c/r8/+/38900

Cherry-pick:
  Reproduce b/133379765: λ group arity mismatch after unused arg removal.
CL: https://r8-review.googlesource.com/c/r8/+/38840

Bug: 133379765
Change-Id: Iea6114b292bde5a0a5970950512f29304582fa1f
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index db8a74d..a07a0ab 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.5.37";
+  public static final String LABEL = "1.5.38";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index 587e172..1595e8e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -41,7 +41,7 @@
 //      -----------------------------------------------------------------------
 //
 // Regular stateless k-style lambda class structure looks like below:
-// NOTE: stateless j-style lambdas do not always have INSTANCE field.
+// NOTE: stateless k-style lambdas do not always have INSTANCE field.
 //
 // -----------------------------------------------------------------------------------------------
 // // signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function2<
@@ -205,7 +205,7 @@
           group.getLambdaIdField(factory),
           id -> group.getCaptureField(factory, id),
           initializerMethod,
-          id.mainMethodProto.parameters.size(),
+          factory.kotlin.functional.getArity(id.iface),
           callerPosition);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 0a85f89..73ab96a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -11,8 +11,11 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
-import java.util.Set;
+import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /** Class provides basic information about symbols related to Kotlin support. */
 public final class Kotlin {
@@ -43,22 +46,24 @@
   }
 
   public final class Functional {
-    private final Set<DexType> functions = Sets.newIdentityHashSet();
+    // NOTE: Kotlin stdlib defines interface Function0 till Function22 explicitly, see:
+    //    https://github.com/JetBrains/kotlin/blob/master/libraries/
+    //                    stdlib/jvm/runtime/kotlin/jvm/functions/Functions.kt
+    //
+    // For functions with arity bigger that 22 it is supposed to use FunctionN as described
+    // in https://github.com/JetBrains/kotlin/blob/master/spec-docs/function-types.md,
+    // but in current implementation (v1.2.21) defining such a lambda results in:
+    //   > "Error: A JNI error has occurred, please check your installation and try again"
+    //
+    // This implementation just ignores lambdas with arity > 22.
+    private final Object2IntMap<DexType> functions = new Object2IntArrayMap<>(
+        IntStream.rangeClosed(0, 22).boxed().collect(
+            Collectors.toMap(
+                i -> factory.createType(addKotlinPrefix("jvm/functions/Function") + i + ";"),
+                Function.identity()))
+    );
 
     private Functional() {
-      // NOTE: Kotlin stdlib defines interface Function0 till Function22 explicitly, see:
-      //    https://github.com/JetBrains/kotlin/blob/master/libraries/
-      //                    stdlib/jvm/runtime/kotlin/jvm/functions/Functions.kt
-      //
-      // For functions with arity bigger that 22 it is supposed to use FunctionN as described
-      // in https://github.com/JetBrains/kotlin/blob/master/spec-docs/function-types.md,
-      // but in current implementation (v1.2.21) defining such a lambda results in:
-      //   > "Error: A JNI error has occurred, please check your installation and try again"
-      //
-      // This implementation just ignores lambdas with arity > 22.
-      for (int i = 0; i <= 22; i++) {
-        functions.add(factory.createType(addKotlinPrefix("jvm/functions/Function") + i + ";"));
-      }
     }
 
     public final DexString kotlinStyleLambdaInstanceName = factory.createString("INSTANCE");
@@ -73,7 +78,13 @@
         factory.constructorMethodName);
 
     public boolean isFunctionInterface(DexType type) {
-      return functions.contains(type);
+      return functions.containsKey(type);
+    }
+
+    public int getArity(DexType type) {
+      assert isFunctionInterface(type)
+          : "Request to retrieve the arity from non-Kotlin-Function: " + type.toSourceString();
+      return functions.getInt(type);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
new file mode 100644
index 0000000..95ec356
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.function.Consumer;
+import org.junit.Test;
+
+public class KotlinUnusedArgumentsInLambdasTest extends AbstractR8KotlinTestBase {
+  private Consumer<InternalOptions> optionsModifier =
+    o -> {
+      o.enableInlining = true;
+      o.enableLambdaMerging = true;
+      o.enableArgumentRemoval = true;
+      o.enableUnusedArgumentRemoval = true;
+    };
+
+  public KotlinUnusedArgumentsInLambdasTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
+  @Test
+  public void testMergingKStyleLambdasAfterUnusedArgumentRemoval() throws Exception {
+    final String mainClassName = "unused_arg_in_lambdas_kstyle.MainKt";
+    runTest("unused_arg_in_lambdas_kstyle", mainClassName, optionsModifier, app -> {
+      CodeInspector inspector = new CodeInspector(app);
+      inspector.forAllClasses(classSubject -> {
+        if (classSubject.getOriginalDescriptor().contains("$ks")) {
+          MethodSubject init = classSubject.init(ImmutableList.of("int"));
+          assertThat(init, isPresent());
+          // Arity 2 should appear.
+          assertTrue(init.iterateInstructions(i -> i.isConstNumber(2)).hasNext());
+
+          MethodSubject invoke = classSubject.uniqueMethodWithName("invoke");
+          assertThat(invoke, isPresent());
+          assertEquals(2, invoke.getMethod().method.proto.parameters.size());
+        }
+      });
+    });
+  }
+
+  @Test
+  public void testMergingJStyleLambdasAfterUnusedArgumentRemoval() throws Exception {
+    final String mainClassName = "unused_arg_in_lambdas_jstyle.MainKt";
+    runTest("unused_arg_in_lambdas_jstyle", mainClassName, optionsModifier, app -> {
+      CodeInspector inspector = new CodeInspector(app);
+      inspector.forAllClasses(classSubject -> {
+        if (classSubject.getOriginalDescriptor().contains("$js")) {
+          MethodSubject get = classSubject.uniqueMethodWithName("get");
+          assertThat(get, isPresent());
+          assertEquals(3, get.getMethod().method.proto.parameters.size());
+        }
+      });
+    });
+  }
+}
diff --git a/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/main.kt b/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/main.kt
index 6cdae60..7fd2d0c 100644
--- a/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/main.kt
+++ b/src/test/kotlinR8TestResources/lambdas_jstyle_trivial/main.kt
@@ -4,7 +4,6 @@
 
 package lambdas_jstyle_trivial
 
-import lambdas_jstyle_trivial.Lambdas
 import lambdas_jstyle_trivial.inner.testInner
 
 private var COUNT = 0
diff --git a/src/test/kotlinR8TestResources/unused_arg_in_lambdas_jstyle/Lambdas.java b/src/test/kotlinR8TestResources/unused_arg_in_lambdas_jstyle/Lambdas.java
new file mode 100644
index 0000000..bb33bd5
--- /dev/null
+++ b/src/test/kotlinR8TestResources/unused_arg_in_lambdas_jstyle/Lambdas.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package unused_arg_in_lambdas_jstyle;
+
+public class Lambdas {
+
+  public interface MultiFunction<R, P1, P2, P3> {
+    R get(P1 a, P2 b, P3 c);
+  }
+
+  public synchronized static <R, P1, P2, P3>
+  void acceptMultiFunction(MultiFunction<R, P1, P2, P3> s, P1 a, P2 b, P3 c) {
+    System.out.println(s.get(a, b, c));
+  }
+}
diff --git a/src/test/kotlinR8TestResources/unused_arg_in_lambdas_jstyle/main.kt b/src/test/kotlinR8TestResources/unused_arg_in_lambdas_jstyle/main.kt
new file mode 100644
index 0000000..3302903
--- /dev/null
+++ b/src/test/kotlinR8TestResources/unused_arg_in_lambdas_jstyle/main.kt
@@ -0,0 +1,25 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package unused_arg_in_lambdas_jstyle
+
+private var COUNT = 0
+
+fun nextInt() = COUNT++
+fun next() = "${nextInt()}".padStart(3, '0')
+
+fun main(args: Array<String>) {
+    multiFunctionLambdaFactory(next(), next(), next())
+}
+
+private data class Local<out T>(val id: T)
+
+private fun <P1, P2, P3> multiFunctionLambdaFactory(a: P1, b: P2, c:P3) {
+    Lambdas.acceptMultiFunction({ x, _, z -> "$x:unused:$z" }, a, b, c)
+    Lambdas.acceptMultiFunction({ x, _, z -> "$x:unused:$z" }, c, a, b)
+    Lambdas.acceptMultiFunction({ x, _, z -> "$x:unused:$z" }, b, c, a)
+
+    Lambdas.acceptMultiFunction({ x, _, z -> "$x:unused:$z" }, Local(a), b, c)
+    Lambdas.acceptMultiFunction({ x, _, z -> "$x:unused:$z" }, Local(b), a, c)
+    Lambdas.acceptMultiFunction({ x, _, z -> "$x:unused:$z" }, Local(c), b, a)
+}
diff --git a/src/test/kotlinR8TestResources/unused_arg_in_lambdas_kstyle/main.kt b/src/test/kotlinR8TestResources/unused_arg_in_lambdas_kstyle/main.kt
new file mode 100644
index 0000000..04f59b6
--- /dev/null
+++ b/src/test/kotlinR8TestResources/unused_arg_in_lambdas_kstyle/main.kt
@@ -0,0 +1,26 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package unused_arg_in_lambdas_kstyle
+
+import kotlin.jvm.internal.TypeIntrinsics
+
+private var COUNT = 11
+
+private fun next() = "${COUNT++}"
+
+fun consumeTwo(l: ((x: Any?, unused: Any?) -> Any)) : Any {
+  // This can be implicitly added by kotlinc
+  TypeIntrinsics.beforeCheckcastToFunctionOfArity(l, 2)
+  return l(next(), next())
+}
+
+private fun lambdaFactory() {
+  println(consumeTwo { x, _ -> x.toString() + "-" })
+  println(consumeTwo { x, _ -> x.toString() + "*" })
+  println(consumeTwo { x, _ -> x.toString() + "+" })
+}
+
+fun main(args: Array<String>) {
+  lambdaFactory()
+}