Make custom conversion methods

- Custom conversion can now be different methods in both ways

Bug: 184026720
Change-Id: I290ed8c5737b15538bc88329bacfb86267746a26
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index e3444a6..1581f83 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
@@ -28,6 +27,7 @@
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.CustomConversionDescriptor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterConstructorCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterVivifiedWrapperCfCodeProvider;
@@ -39,7 +39,6 @@
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringDiagnostic;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -173,13 +172,19 @@
   private DexMethod getCustomConversion(DexType type, DexType srcType, DexType destType) {
     // ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
     // But everything is going to be rewritten, so we need to use vivifiedType and type".
-    Pair<DexType, DexString> pair =
+    CustomConversionDescriptor descriptor =
         appView.options().machineDesugaredLibrarySpecification.getCustomConversions().get(type);
-    if (pair != null) {
-      return factory.createMethod(
-          pair.getFirst(), factory.createProto(destType, srcType), pair.getSecond());
+    if (descriptor == null) {
+      return null;
     }
-    return null;
+    // Because the conversion have rewritten types instead of vivified type we cannot use the
+    // specification content directly until the rewriting is done upfront in the compilation.
+    DexMethod conversion = type == srcType ? descriptor.getTo() : descriptor.getFrom();
+    assert type == srcType
+        ? type == conversion.getReturnType()
+        : type == conversion.getArgumentType(0, true);
+    return factory.createMethod(
+        conversion.getHolderType(), factory.createProto(destType, srcType), conversion.getName());
   }
 
   private boolean canConvert(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java
new file mode 100644
index 0000000..e8c3ce3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/CustomConversionDescriptor.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2022, 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.ir.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.graph.DexMethod;
+
+public class CustomConversionDescriptor {
+  private final DexMethod to;
+  private final DexMethod from;
+
+  public CustomConversionDescriptor(DexMethod to, DexMethod from) {
+    this.to = to;
+    this.from = from;
+    assert to.getReturnType() == from.getArgumentType(0, true);
+    assert from.getReturnType() == to.getArgumentType(0, true);
+  }
+
+  public DexMethod getTo() {
+    return to;
+  }
+
+  public DexMethod getFrom() {
+    return from;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index c06ce2f..adc507f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -6,10 +6,8 @@
 
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.Pair;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -133,7 +131,7 @@
     return rewritingFlags.getDontRetarget();
   }
 
-  public Map<DexType, Pair<DexType, DexString>> getCustomConversions() {
+  public Map<DexType, CustomConversionDescriptor> getCustomConversions() {
     return rewritingFlags.getCustomConversions();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index 240a548..f34634d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -5,9 +5,7 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.Pair;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -35,7 +33,7 @@
       Map<DexType, List<DexMethod>> wrappers,
       Map<DexType, DexType> legacyBackport,
       Set<DexType> dontRetarget,
-      Map<DexType, Pair<DexType, DexString>> customConversions) {
+      Map<DexType, CustomConversionDescriptor> customConversions) {
     this.rewriteType = rewriteType;
     this.rewriteDerivedTypeOnly = rewriteDerivedTypeOnly;
     this.staticRetarget = staticRetarget;
@@ -80,7 +78,7 @@
 
   private final Map<DexType, DexType> legacyBackport;
   private final Set<DexType> dontRetarget;
-  private final Map<DexType, Pair<DexType, DexString>> customConversions;
+  private final Map<DexType, CustomConversionDescriptor> customConversions;
 
   public Map<DexType, DexType> getRewriteType() {
     return rewriteType;
@@ -129,10 +127,14 @@
   }
 
   public boolean isCustomConversionRewrittenType(DexType type) {
-    return Iterables.any(customConversions.values(), pair -> pair.getFirst() == type);
+    return Iterables.any(
+        customConversions.values(),
+        descriptor ->
+            descriptor.getFrom().getHolderType() == type
+                || descriptor.getTo().getHolderType() == type);
   }
 
-  public Map<DexType, Pair<DexType, DexString>> getCustomConversions() {
+  public Map<DexType, CustomConversionDescriptor> getCustomConversions() {
     return customConversions;
   }
 
@@ -178,7 +180,7 @@
     private final ImmutableMap.Builder<DexType, List<DexMethod>> wrappers = ImmutableMap.builder();
     private final ImmutableMap.Builder<DexType, DexType> legacyBackport = ImmutableMap.builder();
     private final ImmutableSet.Builder<DexType> dontRetarget = ImmutableSet.builder();
-    private final ImmutableMap.Builder<DexType, Pair<DexType, DexString>> customConversions =
+    private final ImmutableMap.Builder<DexType, CustomConversionDescriptor> customConversions =
         ImmutableMap.builder();
 
     public void rewriteType(DexType src, DexType target) {
@@ -225,8 +227,12 @@
       dontRetarget.add(type);
     }
 
-    public void putCustomConversion(DexType src, DexType conversionType, DexString conversionName) {
-      customConversions.put(src, new Pair<>(conversionType, conversionName));
+    public void putCustomConversion(DexType src, CustomConversionDescriptor descriptor) {
+      customConversions.put(src, descriptor);
+    }
+
+    public DexType getRewrittenType(DexType type) {
+      return rewriteType.get(type);
     }
 
     public MachineRewritingFlags build() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index 1d5deac..c668858 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -11,11 +11,15 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.CustomConversionDescriptor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
@@ -109,13 +113,31 @@
         .getCustomConversions()
         .forEach(
             (type, conversionType) ->
-                builder.putCustomConversion(
-                    type, conversionType, appInfo.dexItemFactory().convertMethodName));
+                convertCustomConversion(appInfo, builder, type, conversionType));
     rewritingFlags.getDontRetargetLibMember().forEach(builder::addDontRetarget);
     rewritingFlags.getBackportCoreLibraryMember().forEach(builder::putLegacyBackport);
     return builder.build();
   }
 
+  private void convertCustomConversion(
+      AppInfoWithClassHierarchy appInfo,
+      MachineRewritingFlags.Builder builder,
+      DexType type,
+      DexType conversionType) {
+    DexType rewrittenType = builder.getRewrittenType(type);
+    DexProto fromProto = appInfo.dexItemFactory().createProto(rewrittenType, type);
+    DexMethod fromMethod =
+        appInfo
+            .dexItemFactory()
+            .createMethod(conversionType, fromProto, appInfo.dexItemFactory().convertMethodName);
+    DexProto toProto = appInfo.dexItemFactory().createProto(type, rewrittenType);
+    DexMethod toMethod =
+        appInfo
+            .dexItemFactory()
+            .createMethod(conversionType, toProto, appInfo.dexItemFactory().convertMethodName);
+    builder.putCustomConversion(type, new CustomConversionDescriptor(toMethod, fromMethod));
+  }
+
   private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
     ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 2cb9a6f..85efd2b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -131,9 +131,10 @@
     machineDesugaredLibrarySpecification
         .getCustomConversions()
         .forEach(
-            (type, conversionPair) -> {
+            (type, descriptor) -> {
               registerType(appInfo, type);
-              registerType(appInfo, conversionPair.getFirst());
+              registerType(appInfo, descriptor.getTo().getHolderType());
+              registerType(appInfo, descriptor.getFrom().getHolderType());
             });
   }