Merge "Refactor DexItemFactory.createCallSite() to handle altMetafactory()"
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index a681943..f830427 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -46,8 +46,8 @@
     DexString methodName;
     if (lens.isIdentityLens()) {
       methodName = callSite.methodName;
-    } else if (callSite.interfaceMethod != null) {
-      methodName = lens.lookupName(callSite.interfaceMethod);
+    } else if (!callSite.interfaceMethods.isEmpty()) {
+      methodName = lens.lookupName(callSite.interfaceMethods.get(0));
     } else {
       throw new Unimplemented("Minification of non-lambda InvokeDynamic not supported");
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 9e22713..ac954f8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -29,22 +29,27 @@
   public final DexMethodHandle bootstrapMethod;
   public final List<DexValue> bootstrapArgs;
 
-  public final DexMethod interfaceMethod;
+  public final List<DexMethod> interfaceMethods;
 
   private DexEncodedArray encodedArray = null;
 
-  DexCallSite(DexString methodName, DexProto methodProto,
-      DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs, DexMethod interfaceMethod) {
+  DexCallSite(
+      DexString methodName,
+      DexProto methodProto,
+      DexMethodHandle bootstrapMethod,
+      List<DexValue> bootstrapArgs,
+      List<DexMethod> interfaceMethods) {
     assert methodName != null;
     assert methodProto != null;
     assert bootstrapMethod != null;
     assert bootstrapArgs != null;
+    assert interfaceMethods != null;
 
     this.methodName = methodName;
     this.methodProto = methodProto;
     this.bootstrapMethod = bootstrapMethod;
     this.bootstrapArgs = bootstrapArgs;
-    this.interfaceMethod = interfaceMethod;
+    this.interfaceMethods = interfaceMethods;
   }
 
   public static DexCallSite fromAsmInvokeDynamic(
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 75f2ee8..5fe9134 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
 import com.android.tools.r8.graph.DexValue.DexValueMethodType;
 import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.naming.NamingLens;
 import com.google.common.base.Strings;
@@ -26,6 +27,7 @@
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -228,6 +230,33 @@
   public final DexType annotationSynthesizedClassMap =
       createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
 
+  private static final String METAFACTORY_METHOD_NAME = "metafactory";
+  private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
+
+  public final DexType metafactoryType = createType("Ljava/lang/invoke/LambdaMetafactory;");
+  public final DexType callSiteType = createType("Ljava/lang/invoke/CallSite;");
+  public final DexType lookupType = createType("Ljava/lang/invoke/MethodHandles$Lookup;");
+  public final DexType serializableType = createType("Ljava/io/Serializable;");
+
+  public final DexMethod metafactoryMethod =
+      createMethod(
+          metafactoryType,
+          createProto(
+              callSiteType,
+              lookupType,
+              stringType,
+              methodTypeType,
+              methodTypeType,
+              methodHandleType,
+              methodTypeType),
+          createString(METAFACTORY_METHOD_NAME));
+
+  public final DexMethod metafactoryAltMethod =
+      createMethod(
+          metafactoryType,
+          createProto(callSiteType, lookupType, stringType, methodTypeType, objectArrayType),
+          createString(METAFACTORY_ALT_METHOD_NAME));
+
   private boolean skipNameValidationForTesting = false;
 
   public void setSkipNameValidationForTesting(boolean skipNameValidationForTesting) {
@@ -545,23 +574,46 @@
       DexString methodName, DexProto methodProto,
       DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
     assert !sorted;
-    String bootstrapClass = bootstrapMethod.asMethod().holder.toDescriptorString();
-    DexMethod interfaceMethod = null;
-    if (bootstrapClass.equals("Ljava/lang/invoke/LambdaMetafactory;")) {
-      if (methodName.toString().equals("metafactory")) {
-        DexType interfaceType = methodProto.returnType;
-        assert bootstrapArgs.size() == 3;
-        // bootstrapArgs contains samMethodType, implMethod and instantiatedMethodType.
-        DexValueMethodType samMethodType = (DexValueMethodType) bootstrapArgs.get(0);
-        interfaceMethod = createMethod(interfaceType, samMethodType.value, methodName);
-      }
-      // TODO(mathiasr): Support altMetafactory, possibly using ir.desugar.LambdaDescriptor
-    }
+    List<DexMethod> interfaceMethods =
+        getCallSiteInterfaceMethods(methodName, methodProto, bootstrapMethod, bootstrapArgs);
     DexCallSite callSite =
-        new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs, interfaceMethod);
+        new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs, interfaceMethods);
     return canonicalize(callSites, callSite);
   }
 
+  private List<DexMethod> getCallSiteInterfaceMethods(
+      DexString methodName,
+      DexProto methodProto,
+      DexMethodHandle bootstrapMethodHandle,
+      List<DexValue> bootstrapArgs) {
+    // TODO(mathiasr): Unify this with LambdaDescriptor.infer().
+    if (!bootstrapMethodHandle.type.isInvokeStatic()) {
+      return Collections.emptyList();
+    }
+    DexMethod bootstrapMethod = bootstrapMethodHandle.asMethod();
+    if (bootstrapMethod != metafactoryMethod && bootstrapMethod != metafactoryAltMethod) {
+      return Collections.emptyList();
+    }
+    DexType interfaceType = methodProto.returnType;
+    assert bootstrapMethod == metafactoryAltMethod || bootstrapArgs.size() == 3;
+    // Signature of main functional interface method.
+    // In Java docs, this argument is named 'samMethodType'.
+    DexValueMethodType funcErasedSignature = (DexValueMethodType) bootstrapArgs.get(0);
+    DexMethod mainMethod = createMethod(interfaceType, funcErasedSignature.value, methodName);
+    if (bootstrapMethod == metafactoryAltMethod) {
+      List<DexMethod> result = new ArrayList<>();
+      result.add(mainMethod);
+      LambdaDescriptor.extractAltMetafactory(
+          this,
+          bootstrapArgs,
+          type -> result.add(createMethod(type, funcErasedSignature.value, methodName)),
+          bridge -> {});
+      return result;
+    } else {
+      return Collections.singletonList(mainMethod);
+    }
+  }
+
   public DexMethod createMethod(DexString clazzDescriptor, DexString name,
       DexString returnTypeDescriptor,
       DexString[] parameterDescriptors) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 5d52523..49a797a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -21,9 +21,10 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Consumer;
 
 // Represents the lambda descriptor inferred from calls site.
-final class LambdaDescriptor {
+public final class LambdaDescriptor {
   private static final int LAMBDA_ALT_SERIALIZABLE = 1;
   private static final int LAMBDA_ALT_HAS_EXTRA_INTERFACES = 2;
   private static final int LAMBDA_ALT_HAS_BRIDGES = 4;
@@ -227,8 +228,8 @@
     }
 
     DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
-    boolean isMetafactoryMethod = bootstrapMethod == rewriter.metafactoryMethod;
-    boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.metafactoryAltMethod;
+    boolean isMetafactoryMethod = bootstrapMethod == rewriter.factory.metafactoryMethod;
+    boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.factory.metafactoryAltMethod;
     if (!isMetafactoryMethod && !isAltMetafactoryMethod) {
       // It is not a lambda, thus no need to manage this call site.
       return LambdaDescriptor.MATCH_FAILED;
@@ -240,18 +241,18 @@
 
     // Signature of main functional interface method.
     DexValue.DexValueMethodType funcErasedSignature =
-        getBootstrapArgument(callSite, 0, DexValue.DexValueMethodType.class);
+        getBootstrapArgument(callSite.bootstrapArgs, 0, DexValue.DexValueMethodType.class);
 
     // Method handle of the implementation method.
     DexMethodHandle lambdaImplMethodHandle =
-        getBootstrapArgument(callSite, 1, DexValue.DexValueMethodHandle.class).value;
+        getBootstrapArgument(callSite.bootstrapArgs, 1, DexValue.DexValueMethodHandle.class).value;
     // Even though there are some limitations on which method handle kinds are
     // allowed for lambda impl-methods, there is no way to detect unsupported
     // handle kinds after they are transformed into DEX method handle.
 
     // Signature to be enforced on main method.
     DexValue.DexValueMethodType funcEnforcedSignature =
-        getBootstrapArgument(callSite, 2, DexValue.DexValueMethodType.class);
+        getBootstrapArgument(callSite.bootstrapArgs, 2, DexValue.DexValueMethodType.class);
     if (!isEnforcedSignatureValid(
         rewriter, funcEnforcedSignature.value, funcErasedSignature.value)) {
       throw new Unreachable(
@@ -277,67 +278,70 @@
             "Unexpected number of metafactory method arguments in " + callSite.toString());
       }
     } else {
-      extractExtraLambdaInfo(rewriter, callSite, match);
+      extractAltMetafactory(
+          rewriter.factory,
+          callSite.bootstrapArgs,
+          interfaceType -> {
+            if (!match.interfaces.contains(interfaceType)) {
+              match.interfaces.add(interfaceType);
+            }
+          },
+          match.bridges::add);
     }
 
     return match;
   }
 
-  private static void extractExtraLambdaInfo(
-      LambdaRewriter rewriter, DexCallSite callSite, LambdaDescriptor match) {
+  public static void extractAltMetafactory(
+      DexItemFactory dexItemFactory,
+      List<DexValue> bootstrapArgs,
+      Consumer<DexType> interfaceConsumer,
+      Consumer<DexProto> bridgeConsumer) {
     int argIndex = 3;
-    int flagsArg = getBootstrapArgument(
-        callSite, argIndex++, DexValue.DexValueInt.class).value;
+    int flagsArg =
+        getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
     assert (flagsArg & ~LAMBDA_ALT_MASK) == 0;
 
     // Load extra interfaces if any.
     if ((flagsArg & LAMBDA_ALT_HAS_EXTRA_INTERFACES) != 0) {
-      int count = getBootstrapArgument(
-          callSite, argIndex++, DexValue.DexValueInt.class).value;
+      int count = getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
       for (int i = 0; i < count; i++) {
-        DexType type = getBootstrapArgument(
-            callSite, argIndex++, DexValue.DexValueType.class).value;
-        if (!match.interfaces.contains(type)) {
-          match.interfaces.add(type);
-        }
+        DexType interfaceType =
+            getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueType.class).value;
+        interfaceConsumer.accept(interfaceType);
       }
     }
 
     // If the lambda is serializable, add it.
     if ((flagsArg & LAMBDA_ALT_SERIALIZABLE) != 0) {
-      if (!match.interfaces.contains(rewriter.serializableType)) {
-        match.interfaces.add(rewriter.serializableType);
-      }
+      interfaceConsumer.accept(dexItemFactory.serializableType);
     }
 
     // Load bridges if any.
     if ((flagsArg & LAMBDA_ALT_HAS_BRIDGES) != 0) {
-      int count = getBootstrapArgument(
-          callSite, argIndex++, DexValue.DexValueInt.class).value;
+      int count = getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueInt.class).value;
       for (int i = 0; i < count; i++) {
-        DexProto bridgeProto = getBootstrapArgument(
-            callSite, argIndex++, DexValue.DexValueMethodType.class).value;
-        match.bridges.add(bridgeProto);
+        DexProto bridgeProto =
+            getBootstrapArgument(bootstrapArgs, argIndex++, DexValue.DexValueMethodType.class)
+                .value;
+        bridgeConsumer.accept(bridgeProto);
       }
     }
 
-    if (callSite.bootstrapArgs.size() != argIndex) {
-      throw new Unreachable(
-          "Unexpected number of metafactory method arguments in " + callSite.toString());
+    if (bootstrapArgs.size() != argIndex) {
+      throw new Unreachable("Unexpected number of metafactory method arguments in DexCallSite");
     }
   }
 
   @SuppressWarnings("unchecked")
-  private static <T> T getBootstrapArgument(DexCallSite callSite, int i, Class<T> clazz) {
-    List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
+  private static <T> T getBootstrapArgument(List<DexValue> bootstrapArgs, int i, Class<T> clazz) {
     if (bootstrapArgs.size() < i) {
-      throw new Unreachable("Expected to find at least "
-          + i + " bootstrap arguments in " + callSite.toString());
+      throw new Unreachable(
+          "Expected to find at least " + i + " bootstrap arguments in DexCallSite");
     }
     DexValue value = bootstrapArgs.get(i);
     if (!clazz.isAssignableFrom(value.getClass())) {
-      throw new Unreachable("Unexpected type of "
-          + "bootstrap arguments #" + i + " in " + callSite.toString());
+      throw new Unreachable("Unexpected type of bootstrap arguments #" + i + " in DexCallSite");
     }
     return (T) value;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index aad7961..82792df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -41,16 +41,8 @@
  * lambda class generation, and instruction patching.
  */
 public class LambdaRewriter {
-  private static final String METAFACTORY_TYPE_DESCR = "Ljava/lang/invoke/LambdaMetafactory;";
-  private static final String CALLSITE_TYPE_DESCR = "Ljava/lang/invoke/CallSite;";
-  private static final String LOOKUP_TYPE_DESCR = "Ljava/lang/invoke/MethodHandles$Lookup;";
-  private static final String METHODTYPE_TYPE_DESCR = "Ljava/lang/invoke/MethodType;";
-  private static final String METHODHANDLE_TYPE_DESCR = "Ljava/lang/invoke/MethodHandle;";
-  private static final String SERIALIZABLE_TYPE_DESCR = "Ljava/io/Serializable;";
   private static final String SERIALIZED_LAMBDA_TYPE_DESCR = "Ljava/lang/invoke/SerializedLambda;";
 
-  private static final String METAFACTORY_METHOD_NAME = "metafactory";
-  private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
   private static final String DESERIALIZE_LAMBDA_METHOD_NAME = "$deserializeLambda$";
 
   // Public for testing.
@@ -62,12 +54,8 @@
   final AppInfo appInfo;
   final DexItemFactory factory;
 
-  final DexMethod metafactoryMethod;
   final DexMethod objectInitMethod;
 
-  final DexMethod metafactoryAltMethod;
-  final DexType serializableType;
-
   final DexString constructorName;
   final DexString classConstructorName;
   final DexString instanceFieldName;
@@ -97,28 +85,11 @@
     this.factory = converter.appInfo.dexItemFactory;
     this.appInfo = converter.appInfo;
 
-    DexType metafactoryType = factory.createType(METAFACTORY_TYPE_DESCR);
-    DexType callSiteType = factory.createType(CALLSITE_TYPE_DESCR);
-    DexType lookupType = factory.createType(LOOKUP_TYPE_DESCR);
-    DexType methodTypeType = factory.createType(METHODTYPE_TYPE_DESCR);
-    DexType methodHandleType = factory.createType(METHODHANDLE_TYPE_DESCR);
-
-    this.metafactoryMethod = factory.createMethod(metafactoryType,
-        factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
-            methodTypeType, methodHandleType, methodTypeType),
-        factory.createString(METAFACTORY_METHOD_NAME));
-
-    this.metafactoryAltMethod = factory.createMethod(metafactoryType,
-        factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
-            factory.objectArrayType),
-        factory.createString(METAFACTORY_ALT_METHOD_NAME));
-
     this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
     DexProto initProto = factory.createProto(factory.voidType);
     this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
     this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
     this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
-    this.serializableType = factory.createType(SERIALIZABLE_TYPE_DESCR);
 
     this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
     this.deserializeLambdaMethodProto = factory.createProto(