Make eventConsumer type explicit

Bug: 191656218
Change-Id: I7cfa3d04d7e537b3b5faeb570a5b288cb1036d5c
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java
index 199f7de..3e0143a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java
@@ -20,7 +20,7 @@
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer;
-import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APICallbackWrapperCfCodeProvider;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.Sets;
@@ -194,10 +194,9 @@
     DexMethod methodToInstall =
         methodWithVivifiedTypeInSignature(originalMethod.getReference(), clazz.type, appView);
     CfCode cfCode =
-        new APIConverterWrapperCfCodeProvider(
+        new APICallbackWrapperCfCodeProvider(
                 appView,
                 originalMethod.getReference(),
-                null,
                 wrapperSynthesizor,
                 clazz.isInterface(),
                 eventConsumer)
@@ -209,11 +208,8 @@
       newMethod.setLibraryMethodOverride(OptionalBool.TRUE);
     }
     ProgramMethod callback = new ProgramMethod(clazz, newMethod);
-    if (eventConsumer != null) {
-      eventConsumer.acceptAPIConversionCallback(callback);
-    } else {
-      assert appView.enableWholeProgramOptimizations();
-    }
+    assert eventConsumer != null;
+    eventConsumer.acceptAPIConversionCallback(callback);
     return callback;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
index 1396218..be8ac84 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConversionCfCodeProvider;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
@@ -318,7 +319,8 @@
   }
 
   private DexMethod computeReturnConversion(
-      DexMethod invokedMethod, DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
+      DexMethod invokedMethod,
+      DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
     DexType returnType = invokedMethod.proto.returnType;
     if (wrapperSynthesizor.shouldConvert(returnType, invokedMethod)) {
       DexType newReturnType = DesugaredLibraryAPIConverter.vivifiedTypeFor(returnType, appView);
@@ -329,7 +331,8 @@
   }
 
   private DexMethod[] computeParameterConversions(
-      DexMethod invokedMethod, DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
+      DexMethod invokedMethod,
+      DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
     DexMethod[] parameterConversions = new DexMethod[invokedMethod.getArity()];
     DexType[] parameters = invokedMethod.proto.parameters.values;
     for (int i = 0; i < parameters.length; i++) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
index 500df5e..2c246bb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
@@ -123,24 +123,51 @@
     return false;
   }
 
+  public DexMethod internalEnsureConversionMethod(
+      DexType type,
+      DexType srcType,
+      DexType destType,
+      Function<DexClass, WrapperConversions> wrapperConversionsSupplier) {
+    DexMethod customConversion = getCustomConversion(type, srcType, destType);
+    if (customConversion != null) {
+      return customConversion;
+    }
+    assert canGenerateWrapper(type) : type;
+    WrapperConversions wrapperConversions =
+        wrapperConversionsSupplier.apply(getValidClassToWrap(type));
+    return type == srcType
+        ? wrapperConversions.getConversion()
+        : wrapperConversions.getVivifiedConversion();
+  }
+
   public DexMethod ensureConversionMethod(
       DexType type,
       DexType srcType,
       DexType destType,
-      DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
+      DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
+    return internalEnsureConversionMethod(
+        type, srcType, destType, clazz -> ensureWrappers(clazz, eventConsumer));
+  }
+
+  public DexMethod ensureProgramConversionMethod(
+      DexType type,
+      DexType srcType,
+      DexType destType,
+      DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
+    return internalEnsureConversionMethod(
+        type, srcType, destType, clazz -> ensureProgramWrappers(clazz, eventConsumer));
+  }
+
+  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".
     DexType conversionHolder =
         appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type);
-    if (conversionHolder == null) {
-      conversionHolder =
-          type == srcType
-              ? ensureTypeWrapper(type, eventConsumer)
-              : ensureVivifiedTypeWrapper(type, eventConsumer);
+    if (conversionHolder != null) {
+      return factory.createMethod(
+          conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
     }
-    assert conversionHolder != null;
-    return factory.createMethod(
-        conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
+    return null;
   }
 
   private boolean canConvert(DexType type) {
@@ -170,16 +197,6 @@
     return appView.options().desugaredLibraryConfiguration.getWrapperConversions().contains(type);
   }
 
-  private DexType ensureTypeWrapper(
-      DexType type, DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
-    return ensureWrappers(type, eventConsumer).getWrapper().type;
-  }
-
-  private DexType ensureVivifiedTypeWrapper(
-      DexType type, DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
-    return ensureWrappers(type, eventConsumer).getVivifiedWrapper().type;
-  }
-
   private DexClass getValidClassToWrap(DexType type) {
     DexClass dexClass = appView.definitionFor(type);
     // The dexClass should be a library class, so it cannot be null.
@@ -193,100 +210,126 @@
     return DesugaredLibraryAPIConverter.vivifiedTypeFor(type, appView);
   }
 
-  static class Wrappers {
-    private final DexClass wrapper;
-    private final DexClass vivifiedWrapper;
+  static class WrapperConversions {
 
-    Wrappers(DexClass wrapper, DexClass vivifiedWrapper) {
-      this.wrapper = wrapper;
-      this.vivifiedWrapper = vivifiedWrapper;
+    private final DexMethod conversion;
+    private final DexMethod vivifiedConversion;
+
+    WrapperConversions(DexClassAndMethod conversion, DexClassAndMethod vivifiedConversion) {
+      this.conversion = conversion.getReference();
+      this.vivifiedConversion = vivifiedConversion.getReference();
     }
 
-    public DexClass getWrapper() {
-      return wrapper;
+    WrapperConversions(DexMethod conversion, DexMethod vivifiedConversion) {
+      this.conversion = conversion;
+      this.vivifiedConversion = vivifiedConversion;
     }
 
-    public DexClass getVivifiedWrapper() {
-      return vivifiedWrapper;
+    public DexMethod getConversion() {
+      return conversion;
+    }
+
+    public DexMethod getVivifiedConversion() {
+      return vivifiedConversion;
     }
   }
 
-  private Wrappers ensureWrappers(
-      DexType type, DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
-    assert canGenerateWrapper(type) : type;
-    DexClass dexClass = getValidClassToWrap(type);
-    return ensureWrappers(dexClass, eventConsumer);
+  private WrapperConversions ensureProgramWrappers(
+      DexClass context, DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
+    assert eventConsumer != null;
+    assert context.isProgramClass();
+    DexType type = context.type;
+    assert appView.options().isDesugaredLibraryCompilation();
+    DexProgramClass programContext = context.asProgramClass();
+    DexClass wrapper =
+        ensureProgramWrapper(
+            SyntheticKind.WRAPPER,
+            vivifiedTypeFor(type),
+            type,
+            programContext,
+            eventConsumer,
+            wrapperField ->
+                synthesizeVirtualMethodsForTypeWrapper(
+                    programContext, wrapperField, eventConsumer));
+    DexClass vivifiedWrapper =
+        ensureProgramWrapper(
+            SyntheticKind.VIVIFIED_WRAPPER,
+            type,
+            vivifiedTypeFor(type),
+            programContext,
+            eventConsumer,
+            wrapperField ->
+                synthesizeVirtualMethodsForVivifiedTypeWrapper(
+                    programContext, wrapperField, eventConsumer));
+    DexClassAndMethod conversion =
+        ensureProgramConversionMethod(
+            SyntheticKind.WRAPPER, programContext, wrapper, vivifiedWrapper);
+    DexClassAndMethod vivifiedConversion =
+        ensureProgramConversionMethod(
+            SyntheticKind.VIVIFIED_WRAPPER, programContext, vivifiedWrapper, wrapper);
+    return new WrapperConversions(conversion, vivifiedConversion);
   }
 
-  private Wrappers ensureWrappers(
-      DexClass context,
-      DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
+  private WrapperConversions ensureWrappers(
+      DexClass context, DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
     assert eventConsumer != null;
     DexType type = context.type;
-    DexClass wrapper;
-    DexClass vivifiedWrapper;
     if (context.isProgramClass()) {
-      assert appView.options().isDesugaredLibraryCompilation();
-      DexProgramClass programContext = context.asProgramClass();
-      wrapper =
-          ensureProgramWrapper(
-              SyntheticKind.WRAPPER,
-              vivifiedTypeFor(type),
-              type,
-              programContext,
-              eventConsumer,
-              wrapperField ->
-                  synthesizeVirtualMethodsForTypeWrapper(
-                      programContext, wrapperField, eventConsumer));
-      vivifiedWrapper =
-          ensureProgramWrapper(
-              SyntheticKind.VIVIFIED_WRAPPER,
-              type,
-              vivifiedTypeFor(type),
-              programContext,
-              eventConsumer,
-              wrapperField ->
-                  synthesizeVirtualMethodsForVivifiedTypeWrapper(
-                      programContext, wrapperField, eventConsumer));
-      DexField wrapperField = getWrapperUniqueField(wrapper);
-      DexField vivifiedWrapperField = getWrapperUniqueField(vivifiedWrapper);
-      ensureProgramConversionMethod(
-          SyntheticKind.WRAPPER, programContext, wrapperField, vivifiedWrapperField);
-      ensureProgramConversionMethod(
-          SyntheticKind.VIVIFIED_WRAPPER, programContext, vivifiedWrapperField, wrapperField);
-    } else {
-      assert context.isNotProgramClass();
-      ClasspathOrLibraryClass classpathOrLibraryContext = context.asClasspathOrLibraryClass();
-      wrapper =
-          ensureClasspathWrapper(
-              SyntheticKind.WRAPPER,
-              vivifiedTypeFor(type),
-              type,
-              classpathOrLibraryContext,
-              eventConsumer.asClasspathWrapperSynthesizer(),
-              wrapperField ->
-                  synthesizeVirtualMethodsForTypeWrapper(context, wrapperField, eventConsumer));
-      vivifiedWrapper =
-          ensureClasspathWrapper(
-              SyntheticKind.VIVIFIED_WRAPPER,
-              type,
-              vivifiedTypeFor(type),
-              classpathOrLibraryContext,
-              eventConsumer.asClasspathWrapperSynthesizer(),
-              wrapperField ->
-                  synthesizeVirtualMethodsForVivifiedTypeWrapper(
-                      context, wrapperField, eventConsumer));
-      DexField wrapperField = getWrapperUniqueField(wrapper);
-      DexField vivifiedWrapperField = getWrapperUniqueField(vivifiedWrapper);
-      ensureClasspathConversionMethod(
-          SyntheticKind.WRAPPER, classpathOrLibraryContext, wrapperField, vivifiedWrapperField);
-      ensureClasspathConversionMethod(
-          SyntheticKind.VIVIFIED_WRAPPER,
-          classpathOrLibraryContext,
-          vivifiedWrapperField,
-          wrapperField);
+      return getExistingProgramWrapperConversions(context);
     }
-    return new Wrappers(wrapper, vivifiedWrapper);
+    assert context.isNotProgramClass();
+    ClasspathOrLibraryClass classpathOrLibraryContext = context.asClasspathOrLibraryClass();
+    DexClass wrapper =
+        ensureClasspathWrapper(
+            SyntheticKind.WRAPPER,
+            vivifiedTypeFor(type),
+            type,
+            classpathOrLibraryContext,
+            eventConsumer,
+            wrapperField -> synthesizeVirtualMethodsForTypeWrapper(context, wrapperField, null));
+    DexClass vivifiedWrapper =
+        ensureClasspathWrapper(
+            SyntheticKind.VIVIFIED_WRAPPER,
+            type,
+            vivifiedTypeFor(type),
+            classpathOrLibraryContext,
+            eventConsumer,
+            wrapperField ->
+                synthesizeVirtualMethodsForVivifiedTypeWrapper(context, wrapperField, null));
+    DexClassAndMethod conversion =
+        ensureClasspathConversionMethod(
+            SyntheticKind.WRAPPER, classpathOrLibraryContext, wrapper, vivifiedWrapper);
+    DexClassAndMethod vivifiedConversion =
+        ensureClasspathConversionMethod(
+            SyntheticKind.VIVIFIED_WRAPPER, classpathOrLibraryContext, vivifiedWrapper, wrapper);
+    return new WrapperConversions(conversion, vivifiedConversion);
+  }
+
+  private WrapperConversions getExistingProgramWrapperConversions(DexClass context) {
+    DexClass vivifiedWrapper;
+    DexClass wrapper;
+    assert appView.options().isDesugaredLibraryCompilation();
+    wrapper =
+        appView.getSyntheticItems().getExistingFixedClass(SyntheticKind.WRAPPER, context, appView);
+    vivifiedWrapper =
+        appView
+            .getSyntheticItems()
+            .getExistingFixedClass(SyntheticKind.VIVIFIED_WRAPPER, context, appView);
+    DexField wrapperField = getWrapperUniqueField(wrapper);
+    DexField vivifiedWrapperField = getWrapperUniqueField(vivifiedWrapper);
+    DexMethod convertMethod =
+        factory.createMethod(
+            wrapper.type,
+            factory.createProto(vivifiedWrapperField.type, wrapperField.type),
+            factory.convertMethodName);
+    DexMethod vivifiedConvertMethod =
+        factory.createMethod(
+            vivifiedWrapper.type,
+            factory.createProto(wrapperField.type, vivifiedWrapperField.type),
+            factory.convertMethodName);
+    return new WrapperConversions(
+        wrapper.lookupDirectMethod(convertMethod).getReference(),
+        vivifiedWrapper.lookupDirectMethod(vivifiedConvertMethod).getReference());
   }
 
   private DexEncodedField getWrapperUniqueEncodedField(DexClass wrapper) {
@@ -303,7 +346,7 @@
       DexType wrappingType,
       DexType wrappedType,
       DexProgramClass programContext,
-      DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer,
+      DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
       Function<DexEncodedField, DexEncodedMethod[]> virtualMethodProvider) {
     assert appView.options().isDesugaredLibraryCompilation();
     assert eventConsumer != null;
@@ -317,10 +360,7 @@
             // The creation of virtual methods may require new wrappers, this needs to happen
             // once the wrapper is created to avoid infinite recursion.
             wrapper -> {
-              DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer programEventConsumer =
-                  eventConsumer.asProgramWrapperSynthesizer();
-              assert programEventConsumer != null;
-              programEventConsumer.acceptWrapperProgramClass(wrapper);
+              eventConsumer.acceptWrapperProgramClass(wrapper);
               wrapper.setVirtualMethods(
                   virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper)));
             });
@@ -353,10 +393,9 @@
   }
 
   private ProgramMethod ensureProgramConversionMethod(
-      SyntheticKind kind,
-      DexProgramClass context,
-      DexField wrapperField,
-      DexField reverseWrapperField) {
+      SyntheticKind kind, DexProgramClass context, DexClass wrapper, DexClass reverseWrapper) {
+    DexField wrapperField = getWrapperUniqueField(wrapper);
+    DexField reverseWrapperField = getWrapperUniqueField(reverseWrapper);
     return appView
         .getSyntheticItems()
         .ensureFixedClassMethod(
@@ -373,8 +412,10 @@
   private DexClassAndMethod ensureClasspathConversionMethod(
       SyntheticKind kind,
       ClasspathOrLibraryClass context,
-      DexField wrapperField,
-      DexField reverseWrapperField) {
+      DexClass wrapper,
+      DexClass reverseWrapper) {
+    DexField wrapperField = getWrapperUniqueField(wrapper);
+    DexField reverseWrapperField = getWrapperUniqueField(reverseWrapper);
     return appView
         .getSyntheticItems()
         .ensureFixedClasspathClassMethod(
@@ -457,7 +498,7 @@
   private DexEncodedMethod[] synthesizeVirtualMethodsForVivifiedTypeWrapper(
       DexClass dexClass,
       DexEncodedField wrapperField,
-      DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
+      DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
     List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
     List<DexEncodedMethod> generatedMethods = new ArrayList<>();
     // Each method should use only types in their signature, but each method the wrapper forwards
@@ -493,6 +534,7 @@
         finalMethods.add(dexEncodedMethod.getReference());
         continue;
       } else if (dexClass.isProgramClass()) {
+        assert eventConsumer != null;
         cfCode =
             new APIConverterVivifiedWrapperCfCodeProvider(
                     appView,
@@ -515,7 +557,7 @@
   private DexEncodedMethod[] synthesizeVirtualMethodsForTypeWrapper(
       DexClass dexClass,
       DexEncodedField wrapperField,
-      DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
+      DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
     List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
     List<DexEncodedMethod> generatedMethods = new ArrayList<>();
     // Each method should use only vivified types in their signature, but each method the wrapper
@@ -541,6 +583,7 @@
         finalMethods.add(dexEncodedMethod.getReference());
         continue;
       } else if (dexClass.isProgramClass()) {
+        assert eventConsumer != null;
         cfCode =
             new APIConverterWrapperCfCodeProvider(
                     appView,
@@ -678,7 +721,7 @@
         futures.add(
             executorService.submit(
                 () -> {
-                  ensureWrappers(validClassToWrap, eventConsumer);
+                  ensureProgramWrappers(validClassToWrap, eventConsumer);
                   return null;
                 }));
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
index a5e6a15..212ad66 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizerEventConsumer.java
@@ -10,35 +10,12 @@
 
 public interface DesugaredLibraryWrapperSynthesizerEventConsumer {
 
-  default DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer asProgramWrapperSynthesizer() {
-    assert false;
-    return null;
-  }
-
-  default DesugaredLibraryClasspathWrapperSynthesizeEventConsumer asClasspathWrapperSynthesizer() {
-    assert false;
-    return null;
-  }
-
-  interface DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer
-      extends DesugaredLibraryWrapperSynthesizerEventConsumer {
-
-    @Override
-    default DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer asProgramWrapperSynthesizer() {
-      return this;
-    }
+  interface DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer {
 
     void acceptWrapperProgramClass(DexProgramClass clazz);
   }
 
-  interface DesugaredLibraryClasspathWrapperSynthesizeEventConsumer
-      extends DesugaredLibraryWrapperSynthesizerEventConsumer {
-
-    @Override
-    default DesugaredLibraryClasspathWrapperSynthesizeEventConsumer
-        asClasspathWrapperSynthesizer() {
-      return this;
-    }
+  interface DesugaredLibraryClasspathWrapperSynthesizeEventConsumer {
 
     void acceptWrapperClasspathClass(DexClasspathClass clazz);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index 0f265b6..a17976c 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -33,7 +33,8 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizer;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryClasspathWrapperSynthesizeEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.collections.ImmutableDeque;
 import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
@@ -58,7 +59,7 @@
     private final DexMethod forwardMethod;
     private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizer;
     private final boolean itfCall;
-    private final DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer;
+    private final DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer;
 
     public APIConverterVivifiedWrapperCfCodeProvider(
         AppView<?> appView,
@@ -66,7 +67,7 @@
         DexField wrapperField,
         DesugaredLibraryWrapperSynthesizer wrapperSynthesizer,
         boolean itfCall,
-        DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
+        DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
       super(appView, wrapperField.holder);
       this.forwardMethod = forwardMethod;
       this.wrapperField = wrapperField;
@@ -93,7 +94,7 @@
           instructions.add(
               new CfInvoke(
                   Opcodes.INVOKESTATIC,
-                  wrapperSynthesizer.ensureConversionMethod(
+                  wrapperSynthesizer.ensureProgramConversionMethod(
                       param, param, vivifiedTypeFor(param), eventConsumer),
                   false));
           newParameters[index - 1] = vivifiedTypeFor(param);
@@ -125,7 +126,7 @@
         instructions.add(
             new CfInvoke(
                 Opcodes.INVOKESTATIC,
-                wrapperSynthesizer.ensureConversionMethod(
+                wrapperSynthesizer.ensureProgramConversionMethod(
                     returnType, vivifiedTypeFor(returnType), returnType, eventConsumer),
                 false));
       }
@@ -138,31 +139,29 @@
     }
   }
 
-  public static class APIConverterWrapperCfCodeProvider
+  public abstract static class AbstractAPIConverterWrapperCfCodeProvider
       extends DesugaredLibraryAPIConversionCfCodeProvider {
 
-    DexField wrapperField;
     DexMethod forwardMethod;
     DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
     boolean itfCall;
-    private final DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer;
 
-    public APIConverterWrapperCfCodeProvider(
+    public AbstractAPIConverterWrapperCfCodeProvider(
         AppView<?> appView,
+        DexType holder,
         DexMethod forwardMethod,
-        DexField wrapperField,
         DesugaredLibraryWrapperSynthesizer wrapperSynthesizor,
-        boolean itfCall,
-        DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer) {
-      //  Var wrapperField is null if should forward to receiver.
-      super(appView, wrapperField == null ? forwardMethod.holder : wrapperField.holder);
+        boolean itfCall) {
+      super(appView, holder);
       this.forwardMethod = forwardMethod;
-      this.wrapperField = wrapperField;
       this.wrapperSynthesizor = wrapperSynthesizor;
       this.itfCall = itfCall;
-      this.eventConsumer = eventConsumer;
     }
 
+    abstract void generatePushReceiver(List<CfInstruction> instructions);
+
+    abstract DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType);
+
     @Override
     public CfCode generateCfCode() {
       DexItemFactory factory = appView.dexItemFactory();
@@ -170,13 +169,7 @@
       // Wrapped value is a type. Method uses vivifiedTypes as external. Forward method should
       // use types.
 
-      // Var wrapperField is null if should forward to receiver.
-      if (wrapperField == null) {
-        instructions.add(new CfLoad(ValueType.fromDexType(forwardMethod.holder), 0));
-      } else {
-        instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0));
-        instructions.add(new CfFieldInstruction(Opcodes.GETFIELD, wrapperField, wrapperField));
-      }
+      generatePushReceiver(instructions);
       int stackIndex = 1;
       for (DexType param : forwardMethod.proto.parameters.values) {
         instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
@@ -184,8 +177,7 @@
           instructions.add(
               new CfInvoke(
                   Opcodes.INVOKESTATIC,
-                  wrapperSynthesizor.ensureConversionMethod(
-                      param, vivifiedTypeFor(param), param, eventConsumer),
+                  ensureConversionMethod(param, vivifiedTypeFor(param), param),
                   false));
         }
         if (param == factory.longType || param == factory.doubleType) {
@@ -205,8 +197,7 @@
         instructions.add(
             new CfInvoke(
                 Opcodes.INVOKESTATIC,
-                wrapperSynthesizor.ensureConversionMethod(
-                    returnType, returnType, vivifiedTypeFor(returnType), eventConsumer),
+                ensureConversionMethod(returnType, returnType, vivifiedTypeFor(returnType)),
                 false));
         returnType = vivifiedTypeFor(returnType);
       }
@@ -219,6 +210,63 @@
     }
   }
 
+  public static class APICallbackWrapperCfCodeProvider
+      extends AbstractAPIConverterWrapperCfCodeProvider {
+
+    private final DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer;
+
+    public APICallbackWrapperCfCodeProvider(
+        AppView<?> appView,
+        DexMethod forwardMethod,
+        DesugaredLibraryWrapperSynthesizer wrapperSynthesizor,
+        boolean itfCall,
+        DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
+      super(appView, forwardMethod.holder, forwardMethod, wrapperSynthesizor, itfCall);
+      this.eventConsumer = eventConsumer;
+    }
+
+    @Override
+    void generatePushReceiver(List<CfInstruction> instructions) {
+      instructions.add(new CfLoad(ValueType.fromDexType(forwardMethod.holder), 0));
+    }
+
+    @Override
+    DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) {
+      return wrapperSynthesizor.ensureConversionMethod(type, srcType, destType, eventConsumer);
+    }
+  }
+
+  public static class APIConverterWrapperCfCodeProvider
+      extends AbstractAPIConverterWrapperCfCodeProvider {
+
+    private final DexField wrapperField;
+    private final DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer;
+
+    public APIConverterWrapperCfCodeProvider(
+        AppView<?> appView,
+        DexMethod forwardMethod,
+        DexField wrapperField,
+        DesugaredLibraryWrapperSynthesizer wrapperSynthesizor,
+        boolean itfCall,
+        DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
+      super(appView, wrapperField.holder, forwardMethod, wrapperSynthesizor, itfCall);
+      this.wrapperField = wrapperField;
+      this.eventConsumer = eventConsumer;
+    }
+
+    @Override
+    void generatePushReceiver(List<CfInstruction> instructions) {
+      instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0));
+      instructions.add(new CfFieldInstruction(Opcodes.GETFIELD, wrapperField, wrapperField));
+    }
+
+    @Override
+    DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) {
+      return wrapperSynthesizor.ensureProgramConversionMethod(
+          type, srcType, destType, eventConsumer);
+    }
+  }
+
   public static class APIConverterWrapperConversionCfCodeProvider extends SyntheticCfCodeProvider {
 
     DexField reverseWrapperField;