Support generating array conversion during wrapper generation

- Extend the UniqueContext design to support
  creating Unique context outside the main
  parallel processing loop on methods

Bug:222647019
Change-Id: Ibcdbbe5b50595f57b579456743afc6d5c2afd9f6
diff --git a/src/main/java/com/android/tools/r8/contexts/CompilationContext.java b/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
index a34811c..b936f91 100644
--- a/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
+++ b/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.structural.HasherWrapper;
 import java.util.Map;
@@ -96,6 +97,20 @@
       return methodProcessingContext;
     }
 
+    public MainThreadContext createMainThreadContext() {
+      MainThreadContext singleThreadContext = new MainThreadContext(this, parent.mainThread);
+      assert verifyContext(singleThreadContext);
+      return singleThreadContext;
+    }
+
+    public ClassSynthesisDesugaringContext createClassSynthesisDesugaringContext(
+        CfClassSynthesizerDesugaring desugaring) {
+      ClassSynthesisDesugaringContext classSynthesisDesugaringContext =
+          new ClassSynthesisDesugaringContext(this, desugaring);
+      assert verifyContext(classSynthesisDesugaringContext);
+      return classSynthesisDesugaringContext;
+    }
+
     private StringBuilder buildSuffix(StringBuilder builder) {
       return builder.append('$').append(processorId);
     }
@@ -111,14 +126,91 @@
     }
   }
 
-  /** Description of the method context from which to synthesize. */
-  public static class MethodProcessingContext extends ContextDescriptorProvider {
+  /** The processor context is responsible in creating a task context per thread. */
+  public abstract static class ThreadTaskContext extends ContextDescriptorProvider {
     private final ProcessorContext parent;
-    private final ProgramMethod method;
     private int nextId = 0;
 
-    private MethodProcessingContext(ProcessorContext parent, ProgramMethod method) {
+    private ThreadTaskContext(ProcessorContext parent) {
       this.parent = parent;
+    }
+
+    public ProcessorContext getParent() {
+      return parent;
+    }
+
+    int incrementAndGetNextId() {
+      return nextId++;
+    }
+  }
+
+  /**
+   * If no threading is used, the MainThreadContext allows one to generate UniqueContext instances
+   * without a thread specific identifier.
+   */
+  public static class MainThreadContext extends ThreadTaskContext {
+
+    private static final String IDENTIFIER = "main";
+
+    private final Thread mainThread;
+
+    private MainThreadContext(ProcessorContext parent, Thread mainThread) {
+      super(parent);
+      this.mainThread = mainThread;
+    }
+
+    @Override
+    StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
+      return getParent().buildContextDescriptorForTesting(builder).append(IDENTIFIER);
+    }
+
+    @Override
+    StringBuilder buildSyntheticSuffix(StringBuilder builder) {
+      return getParent().buildSyntheticSuffix(builder).append(IDENTIFIER);
+    }
+
+    public UniqueContext createUniqueContext(DexProgramClass context) {
+      assert mainThread == Thread.currentThread() : "Invoked on another thread than main";
+      UniqueContext uniqueContext = new UniqueContext(this, context, incrementAndGetNextId());
+      assert getParent().verifyContext(uniqueContext);
+      return uniqueContext;
+    }
+  }
+
+  public static class ClassSynthesisDesugaringContext extends ThreadTaskContext {
+    private final CfClassSynthesizerDesugaring desugaring;
+
+    private ClassSynthesisDesugaringContext(
+        ProcessorContext parent, CfClassSynthesizerDesugaring desugaring) {
+      super(parent);
+      this.desugaring = desugaring;
+    }
+
+    @Override
+    StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
+      return getParent()
+          .buildContextDescriptorForTesting(builder)
+          .append(desugaring.uniqueIdentifier());
+    }
+
+    @Override
+    StringBuilder buildSyntheticSuffix(StringBuilder builder) {
+      return getParent().buildSyntheticSuffix(builder).append(desugaring.uniqueIdentifier());
+    }
+
+    public UniqueContext createUniqueContext(DexProgramClass context) {
+      UniqueContext uniqueContext = new UniqueContext(this, context, incrementAndGetNextId());
+      assert getParent().verifyContext(uniqueContext);
+      return uniqueContext;
+    }
+  }
+
+  /** Description of the method context from which to synthesize. */
+  public static class MethodProcessingContext extends ThreadTaskContext {
+    private final ProgramMethod method;
+
+    private MethodProcessingContext(ProcessorContext parent, ProgramMethod method) {
+      super(parent);
       this.method = method;
     }
 
@@ -130,12 +222,13 @@
      * deterministic order, eg, by the processing of method instructions being single threaded.
      */
     public UniqueContext createUniqueContext() {
-      UniqueContext uniqueContext = new UniqueContext(this, nextId++);
-      assert parent.verifyContext(uniqueContext);
+      UniqueContext uniqueContext =
+          new UniqueContext(this, getClassContext(), incrementAndGetNextId());
+      assert getParent().verifyContext(uniqueContext);
       return uniqueContext;
     }
 
-    DexProgramClass getClassContext() {
+    private DexProgramClass getClassContext() {
       return method.getHolder();
     }
 
@@ -154,21 +247,26 @@
     StringBuilder buildContextDescriptorForTesting(StringBuilder builder) {
       // Put the type first in the context descriptor.
       builder.append(getClassContext().getType().toDescriptorString());
-      return buildSuffix(parent.buildContextDescriptorForTesting(builder));
+      return buildSuffix(getParent().buildContextDescriptorForTesting(builder));
     }
 
     @Override
     StringBuilder buildSyntheticSuffix(StringBuilder builder) {
-      return buildSuffix(parent.buildSyntheticSuffix(builder));
+      return buildSuffix(getParent().buildSyntheticSuffix(builder));
     }
   }
 
   public static class UniqueContext extends ContextDescriptorProvider {
-    private final MethodProcessingContext parent;
+    private final ThreadTaskContext parent;
+    // The class context is required to:
+    // - determine in which feature the method should be put (feature split),
+    // - put the synthetic in the correct package.
+    private final DexProgramClass classContext;
     private final int positionId;
 
-    private UniqueContext(MethodProcessingContext parent, int positionId) {
+    private UniqueContext(ThreadTaskContext parent, DexProgramClass classContext, int positionId) {
       this.parent = parent;
+      this.classContext = classContext;
       this.positionId = positionId;
     }
 
@@ -187,7 +285,7 @@
     }
 
     public DexProgramClass getClassContext() {
-      return parent.getClassContext();
+      return classContext;
     }
 
     public String getSyntheticSuffix() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaring.java
index 38fa09a..bfe03a9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaring.java
@@ -4,7 +4,13 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
+
 public interface CfClassSynthesizerDesugaring {
 
-  void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer);
+  String uniqueIdentifier();
+
+  void synthesizeClasses(
+      ClassSynthesisDesugaringContext processingContext,
+      CfClassSynthesizerDesugaringEventConsumer eventConsumer);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
index 02c87a6..2e95917 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.desugar;
 
+import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
+import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterL8Synthesizer;
@@ -13,6 +15,7 @@
 import java.util.Collection;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
 
 public abstract class CfClassSynthesizerDesugaringCollection {
 
@@ -38,7 +41,7 @@
     if (synthesizers.isEmpty()) {
       return new EmptyCfClassSynthesizerCollection();
     }
-    return new NonEmptyCfClassSynthesizerCollection(synthesizers);
+    return new NonEmptyCfClassSynthesizerCollection(appView, synthesizers);
   }
 
   public abstract void synthesizeClasses(
@@ -47,11 +50,13 @@
 
   static class NonEmptyCfClassSynthesizerCollection extends CfClassSynthesizerDesugaringCollection {
 
+    private final AppView<?> appView;
     private final Collection<CfClassSynthesizerDesugaring> synthesizers;
 
     public NonEmptyCfClassSynthesizerCollection(
-        Collection<CfClassSynthesizerDesugaring> synthesizers) {
+        AppView<?> appView, Collection<CfClassSynthesizerDesugaring> synthesizers) {
       assert !synthesizers.isEmpty();
+      this.appView = appView;
       this.synthesizers = synthesizers;
     }
 
@@ -59,9 +64,19 @@
     public void synthesizeClasses(
         ExecutorService executorService, CfClassSynthesizerDesugaringEventConsumer eventConsumer)
         throws ExecutionException {
+      assert synthesizers.stream()
+              .map(CfClassSynthesizerDesugaring::uniqueIdentifier)
+              .collect(Collectors.toSet())
+              .size()
+          == synthesizers.size();
+      ProcessorContext processorContext = appView.createProcessorContext();
       ThreadUtils.processItems(
           synthesizers,
-          synthesizer -> synthesizer.synthesizeClasses(eventConsumer),
+          synthesizer -> {
+            ClassSynthesisDesugaringContext classSynthesisDesugaringContext =
+                processorContext.createClassSynthesisDesugaringContext(synthesizer);
+            synthesizer.synthesizeClasses(classSynthesisDesugaringContext, eventConsumer);
+          },
           executorService);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
index 8a9a016..354a323 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
@@ -48,4 +49,9 @@
   public Set<DexProgramClass> getSynthesizedClasses() {
     return synthesizedClasses;
   }
+
+  @Override
+  public void acceptArrayConversion(ProgramMethod arrayConversion) {
+    synthesizedClasses.add(arrayConversion.getHolder());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
index 30fe9d8..450cdb5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
@@ -8,7 +8,7 @@
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.isAPIConversionSyntheticType;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature;
 
-import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.MainThreadContext;
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
@@ -57,8 +57,9 @@
       Collection<DexProgramClass> programClasses,
       CfPostProcessingDesugaringEventConsumer eventConsumer,
       ExecutorService executorService) {
-    assert noPendingWrappersOrConversions();
     ProcessorContext processorContext = appView.createProcessorContext();
+    MainThreadContext mainThreadContext = processorContext.createMainThreadContext();
+    assert noPendingWrappersOrConversions();
     for (DexProgramClass clazz : programClasses) {
       if (!appView.isAlreadyLibraryDesugared(clazz)) {
         ArrayList<DexEncodedMethod> callbacks = new ArrayList<>();
@@ -74,7 +75,7 @@
                     virtualProgramMethod.getDefinition(),
                     virtualProgramMethod.getHolder(),
                     eventConsumer,
-                    processorContext.createMethodProcessingContext(virtualProgramMethod));
+                    mainThreadContext);
             callbacks.add(callback.getDefinition());
           }
         }
@@ -195,7 +196,7 @@
       DexEncodedMethod originalMethod,
       DexProgramClass clazz,
       DesugaredLibraryAPICallbackSynthesizorEventConsumer eventConsumer,
-      MethodProcessingContext methodProcessingContext) {
+      MainThreadContext context) {
     DexMethod methodToInstall =
         methodWithVivifiedTypeInSignature(originalMethod.getReference(), clazz.type, appView);
     CfCode cfCode =
@@ -205,7 +206,7 @@
                 wrapperSynthesizor,
                 clazz.isInterface(),
                 eventConsumer,
-                methodProcessingContext)
+                () -> context.createUniqueContext(clazz))
             .generateCfCode();
     DexEncodedMethod newMethod = wrapperSynthesizor.newSynthesizedMethod(methodToInstall, cfCode);
     newMethod.setCode(cfCode, appView);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index 684f9bc..520b5ea 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -296,7 +296,11 @@
     if (wrapperSynthesizor.shouldConvert(returnType, invokedMethod, context)) {
       DexType newReturnType = DesugaredLibraryAPIConverter.vivifiedTypeFor(returnType, appView);
       return wrapperSynthesizor.ensureConversionMethod(
-          returnType, newReturnType, returnType, eventConsumer, methodProcessingContext);
+          returnType,
+          newReturnType,
+          returnType,
+          eventConsumer,
+          methodProcessingContext::createUniqueContext);
     }
     return null;
   }
@@ -314,7 +318,11 @@
         DexType argVivifiedType = vivifiedTypeFor(argType, appView);
         parameterConversions[i] =
             wrapperSynthesizor.ensureConversionMethod(
-                argType, argType, argVivifiedType, eventConsumer, methodProcessingContext);
+                argType,
+                argType,
+                argVivifiedType,
+                eventConsumer,
+                methodProcessingContext::createUniqueContext);
       }
     }
     return parameterConversions;
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 1bd5ba8..e1fbb96 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
@@ -5,7 +5,8 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion;
 
 import com.android.tools.r8.androidapi.ComputedApiLevel;
-import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
+import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
@@ -49,6 +50,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
+import java.util.function.Supplier;
 
 // I am responsible for the generation of wrappers used to call library APIs when desugaring
 // libraries. Wrappers can be both ways, wrapping the desugarType as a type, or the type as
@@ -133,10 +135,9 @@
       DexType srcType,
       DexType destType,
       DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer,
-      MethodProcessingContext methodProcessingContext) {
+      Supplier<UniqueContext> contextSupplier) {
     if (type.isArrayType()) {
-      return ensureArrayConversionMethod(
-          type, srcType, destType, eventConsumer, methodProcessingContext);
+      return ensureArrayConversionMethod(type, srcType, destType, eventConsumer, contextSupplier);
     }
     DexMethod customConversion = getCustomConversion(type, srcType, destType);
     if (customConversion != null) {
@@ -162,20 +163,47 @@
       DexType srcType,
       DexType destType,
       DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer,
-      MethodProcessingContext methodProcessingContext) {
+      Supplier<UniqueContext> contextSupplier) {
     DexMethod conversion =
         ensureConversionMethod(
             type.toDimensionMinusOneType(factory),
             srcType.toDimensionMinusOneType(factory),
             destType.toDimensionMinusOneType(factory),
             eventConsumer,
-            methodProcessingContext);
+            contextSupplier);
+    return ensureArrayConversionMethod(
+        srcType, destType, eventConsumer, contextSupplier, conversion);
+  }
+
+  private DexMethod ensureArrayConversionMethodFromExistingBaseConversion(
+      DexType type,
+      DexType srcType,
+      DexType destType,
+      DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
+      Supplier<UniqueContext> contextSupplier) {
+    DexMethod conversion =
+        getExistingProgramConversionMethod(
+            type.toDimensionMinusOneType(factory),
+            srcType.toDimensionMinusOneType(factory),
+            destType.toDimensionMinusOneType(factory),
+            eventConsumer,
+            contextSupplier);
+    return ensureArrayConversionMethod(
+        srcType, destType, eventConsumer, contextSupplier, conversion);
+  }
+
+  private DexMethod ensureArrayConversionMethod(
+      DexType srcType,
+      DexType destType,
+      DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer,
+      Supplier<UniqueContext> contextSupplier,
+      DexMethod conversion) {
     ProgramMethod arrayConversion =
         appView
             .getSyntheticItems()
             .createMethod(
                 SyntheticKind.ARRAY_CONVERSION,
-                methodProcessingContext.createUniqueContext(),
+                contextSupplier.get(),
                 appView,
                 builder ->
                     builder
@@ -195,8 +223,15 @@
   }
 
   public DexMethod getExistingProgramConversionMethod(
-      DexType type, DexType srcType, DexType destType) {
-    assert !type.isArrayType();
+      DexType type,
+      DexType srcType,
+      DexType destType,
+      DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
+      Supplier<UniqueContext> contextSupplier) {
+    if (type.isArrayType()) {
+      return ensureArrayConversionMethodFromExistingBaseConversion(
+          type, srcType, destType, eventConsumer, contextSupplier);
+    }
     DexMethod customConversion = getCustomConversion(type, srcType, destType);
     if (customConversion != null) {
       return customConversion;
@@ -321,7 +356,11 @@
             type,
             classpathOrLibraryContext,
             eventConsumer,
-            wrapperField -> synthesizeVirtualMethodsForTypeWrapper(context, methods, wrapperField));
+            wrapperField ->
+                synthesizeVirtualMethodsForTypeWrapper(
+                    methods,
+                    wrapperField,
+                    DesugaredLibraryWrapperSynthesizer::codeForClasspathMethod));
     DexClass vivifiedWrapper =
         ensureClasspathWrapper(
             SyntheticKind.VIVIFIED_WRAPPER,
@@ -330,7 +369,10 @@
             classpathOrLibraryContext,
             eventConsumer,
             wrapperField ->
-                synthesizeVirtualMethodsForVivifiedTypeWrapper(context, methods, wrapperField));
+                synthesizeVirtualMethodsForVivifiedTypeWrapper(
+                    methods,
+                    wrapperField,
+                    DesugaredLibraryWrapperSynthesizer::codeForClasspathMethod));
     return new WrapperConversions(
         getConversion(wrapper, vivifiedType, type),
         getConversion(vivifiedWrapper, type, vivifiedType));
@@ -494,82 +536,75 @@
                     .generateCfCode());
   }
 
+  private static CfCode codeForClasspathMethod(DexMethod ignore) {
+    return null;
+  }
+
   private Collection<DexEncodedMethod> synthesizeVirtualMethodsForVivifiedTypeWrapper(
-      DexClass dexClass, Iterable<DexMethod> allImplementedMethods, DexEncodedField wrapperField) {
+      Iterable<DexMethod> allImplementedMethods,
+      DexEncodedField wrapperField,
+      Function<DexMethod, CfCode> cfCodeProvider) {
     List<DexEncodedMethod> generatedMethods = new ArrayList<>();
-    // Each method should use only types in their signature, but each method the wrapper forwards
-    // to should used only vivified types.
-    // Generated method looks like:
-    // long foo (type, int)
-    //   v0 <- arg0;
-    //   v1 <- arg1;
-    //   v2 <- convertTypeToVivifiedType(v0);
-    //   v3 <- wrappedValue.foo(v2,v1);
-    //   return v3;
     for (DexMethod method : allImplementedMethods) {
-      DexClass holderClass = appView.definitionFor(method.getHolderType());
-      boolean isInterface;
-      if (holderClass == null) {
-        assert appView
-            .options()
-            .machineDesugaredLibrarySpecification
-            .isEmulatedInterfaceRewrittenType(method.getHolderType());
-        isInterface = true;
-      } else {
-        isInterface = holderClass.isInterface();
-      }
       DexMethod methodToInstall =
           factory.createMethod(wrapperField.getHolderType(), method.proto, method.name);
-      CfCode cfCode;
-      if (dexClass.isProgramClass()) {
-        cfCode =
-            new APIConverterVivifiedWrapperCfCodeProvider(
-                    appView, methodToInstall, wrapperField.getReference(), this, isInterface)
-                .generateCfCode();
-      } else {
-        cfCode = null;
-      }
+      CfCode cfCode = cfCodeProvider.apply(method);
       DexEncodedMethod newDexEncodedMethod = newSynthesizedMethod(methodToInstall, cfCode);
       generatedMethods.add(newDexEncodedMethod);
     }
     return generatedMethods;
   }
 
+  private CfCode synthesizeCfCodeForVivifiedTypeWrapper(
+      DexMethod method,
+      DexField wrapperField,
+      DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
+      Supplier<UniqueContext> contextSupplier) {
+    DexClass holderClass = appView.definitionFor(method.getHolderType());
+    boolean isInterface;
+    if (holderClass == null) {
+      assert appView
+          .options()
+          .machineDesugaredLibrarySpecification
+          .isEmulatedInterfaceRewrittenType(method.getHolderType());
+      isInterface = true;
+    } else {
+      isInterface = holderClass.isInterface();
+    }
+    return new APIConverterVivifiedWrapperCfCodeProvider(
+            appView, method, wrapperField, this, isInterface, eventConsumer, contextSupplier)
+        .generateCfCode();
+  }
+
   private Collection<DexEncodedMethod> synthesizeVirtualMethodsForTypeWrapper(
-      DexClass dexClass, Iterable<DexMethod> dexMethods, DexEncodedField wrapperField) {
+      Iterable<DexMethod> dexMethods,
+      DexEncodedField wrapperField,
+      Function<DexMethod, CfCode> cfCodeProvider) {
     List<DexEncodedMethod> generatedMethods = new ArrayList<>();
-    // Each method should use only vivified types in their signature, but each method the wrapper
-    // forwards
-    // to should used only types.
-    // Generated method looks like:
-    // long foo (type, int)
-    //   v0 <- arg0;
-    //   v1 <- arg1;
-    //   v2 <- convertVivifiedTypeToType(v0);
-    //   v3 <- wrappedValue.foo(v2,v1);
-    //   return v3;
     for (DexMethod method : dexMethods) {
-      DexClass holderClass = appView.definitionFor(method.getHolderType());
-      assert holderClass != null || appView.options().isDesugaredLibraryCompilation();
-      boolean isInterface = holderClass == null || holderClass.isInterface();
       DexMethod methodToInstall =
           DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
               method, wrapperField.getHolderType(), appView);
-      CfCode cfCode;
-      if (dexClass.isProgramClass()) {
-        cfCode =
-            new APIConverterWrapperCfCodeProvider(
-                    appView, method, wrapperField.getReference(), this, isInterface)
-                .generateCfCode();
-      } else {
-        cfCode = null;
-      }
+      CfCode cfCode = cfCodeProvider.apply(method);
       DexEncodedMethod newDexEncodedMethod = newSynthesizedMethod(methodToInstall, cfCode);
       generatedMethods.add(newDexEncodedMethod);
     }
     return generatedMethods;
   }
 
+  private CfCode synthesizeCfCodeForTypeWrapper(
+      DexMethod method,
+      DexField wrapperField,
+      DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
+      Supplier<UniqueContext> contextSupplier) {
+    DexClass holderClass = appView.definitionFor(method.getHolderType());
+    assert holderClass != null || appView.options().isDesugaredLibraryCompilation();
+    boolean isInterface = holderClass == null || holderClass.isInterface();
+    return new APIConverterWrapperCfCodeProvider(
+            appView, method, wrapperField, this, isInterface, eventConsumer, contextSupplier)
+        .generateCfCode();
+  }
+
   DexEncodedMethod newSynthesizedMethod(DexMethod methodToInstall, Code code) {
     MethodAccessFlags newFlags =
         MethodAccessFlags.fromSharedAccessFlags(
@@ -609,12 +644,19 @@
         .build();
   }
 
+  @Override
+  public String uniqueIdentifier() {
+    return "$wrapper$";
+  }
+
   // Program wrappers are harder to deal with than classpath wrapper because generating a method's
   // code may require other wrappers. To keep it simple (This is L8 specific), we generate first
   // the wrappers with the conversion methods only, then the virtual methods assuming the
   // conversion methods are present.
   @Override
-  public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+  public void synthesizeClasses(
+      ClassSynthesisDesugaringContext processingContext,
+      CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     MachineDesugaredLibrarySpecification librarySpecification =
         appView.options().machineDesugaredLibrarySpecification;
     Map<DexProgramClass, Iterable<DexMethod>> validClassesToWrap = new IdentityHashMap<>();
@@ -636,7 +678,9 @@
                 }
               }
             });
-    validClassesToWrap.forEach(this::ensureProgramWrappersVirtualMethods);
+    validClassesToWrap.forEach(
+        (clazz, methods) ->
+            ensureProgramWrappersVirtualMethods(clazz, methods, eventConsumer, processingContext));
   }
 
   // We generate first the two wrappers with the constructor method and the fields, then we
@@ -664,16 +708,35 @@
         SyntheticKind.VIVIFIED_WRAPPER, programContext, vivifiedWrapper, wrapper);
   }
 
-  private void ensureProgramWrappersVirtualMethods(DexClass context, Iterable<DexMethod> methods) {
-    assert context.isProgramClass();
+  private void ensureProgramWrappersVirtualMethods(
+      DexProgramClass context,
+      Iterable<DexMethod> methods,
+      CfClassSynthesizerDesugaringEventConsumer eventConsumer,
+      ClassSynthesisDesugaringContext processingContext) {
     DexProgramClass wrapper = getExistingProgramWrapper(context, SyntheticKind.WRAPPER);
+    DexEncodedField wrapperField = getWrapperUniqueEncodedField(wrapper);
     wrapper.addVirtualMethods(
         synthesizeVirtualMethodsForTypeWrapper(
-            context, methods, getWrapperUniqueEncodedField(wrapper)));
+            methods,
+            wrapperField,
+            m ->
+                synthesizeCfCodeForTypeWrapper(
+                    m,
+                    wrapperField.getReference(),
+                    eventConsumer,
+                    () -> processingContext.createUniqueContext(wrapper))));
     DexProgramClass vivifiedWrapper =
         getExistingProgramWrapper(context, SyntheticKind.VIVIFIED_WRAPPER);
+    DexEncodedField vivifiedWrapperField = getWrapperUniqueEncodedField(vivifiedWrapper);
     vivifiedWrapper.addVirtualMethods(
         synthesizeVirtualMethodsForVivifiedTypeWrapper(
-            context, methods, getWrapperUniqueEncodedField(vivifiedWrapper)));
+            methods,
+            vivifiedWrapperField,
+            m ->
+                synthesizeCfCodeForVivifiedTypeWrapper(
+                    m,
+                    vivifiedWrapperField.getReference(),
+                    eventConsumer,
+                    () -> processingContext.createUniqueContext(vivifiedWrapper))));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java
index 562c5ac..cf803cb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java
@@ -10,20 +10,22 @@
 
 public interface DesugaredLibraryWrapperSynthesizerEventConsumer {
 
-  interface DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer {
+  void acceptArrayConversion(ProgramMethod arrayConversion);
+
+  interface DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer
+      extends DesugaredLibraryWrapperSynthesizerEventConsumer {
 
     void acceptWrapperProgramClass(DexProgramClass clazz);
 
     void acceptEnumConversionProgramClass(DexProgramClass clazz);
   }
 
-  interface DesugaredLibraryClasspathWrapperSynthesizeEventConsumer {
+  interface DesugaredLibraryClasspathWrapperSynthesizeEventConsumer
+      extends DesugaredLibraryWrapperSynthesizerEventConsumer {
 
     void acceptWrapperClasspathClass(DexClasspathClass clazz);
 
     void acceptEnumConversionClasspathClass(DexClasspathClass clazz);
-
-    void acceptArrayConversion(ProgramMethod arrayConversion);
   }
 
   interface DesugaredLibraryAPIConverterEventConsumer
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
index 60086f8..6a32412 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
 
+import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
@@ -33,7 +34,14 @@
   }
 
   @Override
-  public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+  public String uniqueIdentifier() {
+    return "$retargeter$";
+  }
+
+  @Override
+  public void synthesizeClasses(
+      ClassSynthesisDesugaringContext processingContext,
+      CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget =
         appView.options().machineDesugaredLibrarySpecification.getEmulatedVirtualRetarget();
     for (EmulatedDispatchMethodDescriptor emulatedDispatchMethod :
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 7e8a2cc..8d14980 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -3,24 +3,22 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.desugar.itf;
 
+import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 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.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
 import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
 import com.android.tools.r8.utils.StringDiagnostic;
 import java.util.LinkedHashMap;
@@ -75,17 +73,6 @@
                         methodBuilder)));
   }
 
-  private DexMethod emulatedMethod(DerivedMethod method, DexType holder) {
-    assert method.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
-    DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
-    return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
-  }
-
-  private DexMethod interfaceMethod(DerivedMethod method) {
-    assert method.getHolderKind() == null;
-    return method.getMethod();
-  }
-
   private void synthesizeEmulatedInterfaceMethod(
       ProgramMethod method,
       EmulatedDispatchMethodDescriptor descriptor,
@@ -127,7 +114,14 @@
   }
 
   @Override
-  public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+  public String uniqueIdentifier() {
+    return "$emulatedInterface$";
+  }
+
+  @Override
+  public void synthesizeClasses(
+      ClassSynthesisDesugaringContext processingContext,
+      CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     assert appView.options().isDesugaredLibraryCompilation();
     appView
         .options()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index 9a7c53c..a6fcf97 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.cf.code.CfInvokeDynamic;
 import com.android.tools.r8.cf.code.CfStackInstruction;
 import com.android.tools.r8.cf.code.CfTypeInstruction;
+import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
@@ -476,7 +477,14 @@
   }
 
   @Override
-  public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+  public String uniqueIdentifier() {
+    return "$record$";
+  }
+
+  @Override
+  public void synthesizeClasses(
+      ClassSynthesisDesugaringContext processingContext,
+      CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     if (appView.appInfo().app().getFlags().hasReadRecordReferenceFromProgramClass()) {
       ensureRecordClass(eventConsumer);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
index 1b5b27c..476859a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
@@ -62,7 +62,7 @@
       return null;
     }
     if (isTwrCloseResourceInvoke(instruction)) {
-      return rewriteTwrCloseResourceInvoke(eventConsumer, context, methodProcessingContext);
+      return rewriteTwrCloseResourceInvoke(eventConsumer, methodProcessingContext);
     }
     if (!appView.options().canUseSuppressedExceptions()) {
       if (isTwrSuppressedInvoke(instruction, addSuppressed)) {
@@ -108,7 +108,6 @@
 
   private ImmutableList<CfInstruction> rewriteTwrCloseResourceInvoke(
       CfInstructionDesugaringEventConsumer eventConsumer,
-      ProgramMethod context,
       MethodProcessingContext methodProcessingContext) {
     // Synthesize a new method.
     return createAndCallSyntheticMethod(
@@ -117,7 +116,7 @@
         BackportedMethods::CloseResourceMethod_closeResourceImpl,
         methodProcessingContext,
         eventConsumer::acceptTwrCloseResourceMethod,
-        context);
+        methodProcessingContext.getMethodContext());
   }
 
   private ImmutableList<CfInstruction> createAndCallSyntheticMethod(
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 399648b..9402a62 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,7 @@
 import com.android.tools.r8.cf.code.CfStaticFieldRead;
 import com.android.tools.r8.cf.code.CfStore;
 import com.android.tools.r8.cf.code.CfThrow;
-import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -50,12 +50,14 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizer;
 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.utils.BooleanUtils;
 import com.android.tools.r8.utils.collections.ImmutableDeque;
 import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.function.Supplier;
 import org.objectweb.asm.Opcodes;
 
 public abstract class DesugaredLibraryAPIConversionCfCodeProvider extends SyntheticCfCodeProvider {
@@ -75,18 +77,24 @@
     private final DexMethod forwardMethod;
     private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizer;
     private final boolean itfCall;
+    private final DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer;
+    private final Supplier<UniqueContext> contextSupplier;
 
     public APIConverterVivifiedWrapperCfCodeProvider(
         AppView<?> appView,
         DexMethod forwardMethod,
         DexField wrapperField,
         DesugaredLibraryWrapperSynthesizer wrapperSynthesizer,
-        boolean itfCall) {
+        boolean itfCall,
+        DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
+        Supplier<UniqueContext> contextSupplier) {
       super(appView, wrapperField.holder);
       this.forwardMethod = forwardMethod;
       this.wrapperField = wrapperField;
       this.wrapperSynthesizer = wrapperSynthesizer;
       this.itfCall = itfCall;
+      this.eventConsumer = eventConsumer;
+      this.contextSupplier = contextSupplier;
     }
 
     @Override
@@ -107,8 +115,7 @@
           instructions.add(
               new CfInvoke(
                   Opcodes.INVOKESTATIC,
-                  wrapperSynthesizer.getExistingProgramConversionMethod(
-                      param, param, vivifiedTypeFor(param)),
+                  conversionMethod(param, param, vivifiedTypeFor(param)),
                   false));
           newParameters[index - 1] = vivifiedTypeFor(param);
         }
@@ -139,8 +146,7 @@
         instructions.add(
             new CfInvoke(
                 Opcodes.INVOKESTATIC,
-                wrapperSynthesizer.getExistingProgramConversionMethod(
-                    returnType, vivifiedTypeFor(returnType), returnType),
+                conversionMethod(returnType, vivifiedTypeFor(returnType), returnType),
                 false));
       }
       if (returnType == factory.voidType) {
@@ -150,6 +156,11 @@
       }
       return standardCfCodeFromInstructions(instructions);
     }
+
+    private DexMethod conversionMethod(DexType type, DexType srcType, DexType destType) {
+      return wrapperSynthesizer.getExistingProgramConversionMethod(
+          type, srcType, destType, eventConsumer, contextSupplier);
+    }
   }
 
   public abstract static class AbstractAPIConverterWrapperCfCodeProvider
@@ -227,7 +238,7 @@
       extends AbstractAPIConverterWrapperCfCodeProvider {
 
     private final DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer;
-    private final MethodProcessingContext methodProcessingContext;
+    private final Supplier<UniqueContext> contextSupplier;
 
     public APICallbackWrapperCfCodeProvider(
         AppView<?> appView,
@@ -235,10 +246,10 @@
         DesugaredLibraryWrapperSynthesizer wrapperSynthesizor,
         boolean itfCall,
         DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer,
-        MethodProcessingContext methodProcessingContext) {
+        Supplier<UniqueContext> contextSupplier) {
       super(appView, forwardMethod.holder, forwardMethod, wrapperSynthesizor, itfCall);
       this.eventConsumer = eventConsumer;
-      this.methodProcessingContext = methodProcessingContext;
+      this.contextSupplier = contextSupplier;
     }
 
     @Override
@@ -249,7 +260,7 @@
     @Override
     DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) {
       return wrapperSynthesizor.ensureConversionMethod(
-          type, srcType, destType, eventConsumer, methodProcessingContext);
+          type, srcType, destType, eventConsumer, contextSupplier);
     }
   }
 
@@ -257,15 +268,21 @@
       extends AbstractAPIConverterWrapperCfCodeProvider {
 
     private final DexField wrapperField;
+    private final DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer;
+    private final Supplier<UniqueContext> contextSupplier;
 
     public APIConverterWrapperCfCodeProvider(
         AppView<?> appView,
         DexMethod forwardMethod,
         DexField wrapperField,
         DesugaredLibraryWrapperSynthesizer wrapperSynthesizor,
-        boolean itfCall) {
+        boolean itfCall,
+        DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer,
+        Supplier<UniqueContext> contextSupplier) {
       super(appView, wrapperField.holder, forwardMethod, wrapperSynthesizor, itfCall);
       this.wrapperField = wrapperField;
+      this.eventConsumer = eventConsumer;
+      this.contextSupplier = contextSupplier;
     }
 
     @Override
@@ -276,7 +293,8 @@
 
     @Override
     DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) {
-      return wrapperSynthesizor.getExistingProgramConversionMethod(type, srcType, destType);
+      return wrapperSynthesizor.getExistingProgramConversionMethod(
+          type, srcType, destType, eventConsumer, contextSupplier);
     }
   }