DesugaredLibraryRetargeter cf to cf in D8

Bug: 188767735
Change-Id: Id66cba48eb859bb23f23e1b73a27807e1f68c7ff
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 8f83dca..83e0ff5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -107,6 +107,9 @@
         assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
       }
 
+      converter.finalizeDesugaredLibraryRetargeting(instructionDesugaringEventConsumer);
+      assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
+
       classes = deferred;
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 1843cea..5dd5114 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -222,10 +222,7 @@
       assert options.desugarState.isOn();
       this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
       this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
-      this.desugaredLibraryRetargeter =
-          options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
-              ? null
-              : new DesugaredLibraryRetargeter(appView);
+      this.desugaredLibraryRetargeter = null; // Managed cf to cf.
       this.interfaceMethodRewriter =
           options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
               ? null
@@ -258,14 +255,15 @@
             ? CfInstructionDesugaringCollection.empty()
             : CfInstructionDesugaringCollection.create(appView);
     this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
+    this.desugaredLibraryRetargeter =
+        options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
+                || !appView.enableWholeProgramOptimizations()
+            ? null
+            : new DesugaredLibraryRetargeter(appView);
     this.interfaceMethodRewriter =
         options.isInterfaceMethodDesugaringEnabled()
             ? new InterfaceMethodRewriter(appView, this)
             : null;
-    this.desugaredLibraryRetargeter =
-        options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
-            ? null
-            : new DesugaredLibraryRetargeter(appView);
     this.covariantReturnTypeAnnotationTransformer =
         options.processCovariantReturnTypeAnnotations
             ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
@@ -371,6 +369,12 @@
         D8NestBasedAccessDesugaring::clearNestAttributes);
   }
 
+  public void finalizeDesugaredLibraryRetargeting(
+      D8CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer) {
+    instructionDesugaring.withDesugaredLibraryRetargeter(
+        retargeter -> retargeter.finalizeDesugaring(instructionDesugaringEventConsumer));
+  }
+
   private void staticizeClasses(
       OptimizationFeedback feedback, ExecutorService executorService, GraphLens applied)
       throws ExecutionException {
@@ -397,12 +401,13 @@
     }
   }
 
-  private void synthesizeRetargetClass(ExecutorService executorService) throws ExecutionException {
+  private void synthesizeRetargetClass() throws ExecutionException {
     if (desugaredLibraryRetargeter != null) {
-      desugaredLibraryRetargeter.synthesizeRetargetClasses(executorService, this);
+      desugaredLibraryRetargeter.synthesizeRetargetClasses();
     }
   }
 
+
   private void synthesizeEnumUnboxingUtilityMethods(ExecutorService executorService)
       throws ExecutionException {
     if (enumUnboxer != null) {
@@ -441,7 +446,6 @@
     Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
 
     desugarInterfaceMethods(builder, ExcludeDexResources, executor);
-    synthesizeRetargetClass(executor);
     processCovariantReturnTypeAnnotations(builder);
     generateDesugaredLibraryAPIWrappers(builder, executor);
 
@@ -587,7 +591,6 @@
           new NeedsIRDesugarUseRegistry(
               method,
               appView,
-              desugaredLibraryRetargeter,
               interfaceMethodRewriter,
               desugaredLibraryAPIConverter);
       method.registerCodeReferences(useRegistry);
@@ -779,7 +782,7 @@
     feedback.updateVisibleOptimizationInfo();
 
     printPhase("Utility classes synthesis");
-    synthesizeRetargetClass(executorService);
+    synthesizeRetargetClass();
     synthesizeEnumUnboxingUtilityMethods(executorService);
 
     printPhase("Desugared library API Conversion finalization");
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
index bbc0bcb..5a4dec1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
@@ -19,26 +19,22 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 
 class NeedsIRDesugarUseRegistry extends UseRegistry {
 
   private boolean needsDesugaring = false;
   private final ProgramMethod context;
-  private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
   private final InterfaceMethodRewriter interfaceMethodRewriter;
   private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
 
   public NeedsIRDesugarUseRegistry(
       ProgramMethod method,
       AppView<?> appView,
-      DesugaredLibraryRetargeter desugaredLibraryRetargeter,
       InterfaceMethodRewriter interfaceMethodRewriter,
       DesugaredLibraryAPIConverter desugaredLibraryAPIConverter) {
     super(appView.dexItemFactory());
     this.context = method;
-    this.desugaredLibraryRetargeter = desugaredLibraryRetargeter;
     this.interfaceMethodRewriter = interfaceMethodRewriter;
     this.desugaredLibraryAPIConverter = desugaredLibraryAPIConverter;
   }
@@ -58,14 +54,12 @@
 
   @Override
   public void registerInvokeVirtual(DexMethod method) {
-    registerLibraryRetargeting(method, false);
     registerInterfaceMethodRewriting(method, VIRTUAL);
     registerDesugaredLibraryAPIConverter(method);
   }
 
   @Override
   public void registerInvokeDirect(DexMethod method) {
-    registerLibraryRetargeting(method, false);
     registerInterfaceMethodRewriting(method, DIRECT);
     registerDesugaredLibraryAPIConverter(method);
   }
@@ -86,24 +80,14 @@
     }
   }
 
-  private void registerLibraryRetargeting(DexMethod method, boolean b) {
-    if (!needsDesugaring) {
-      needsDesugaring =
-          desugaredLibraryRetargeter != null
-              && desugaredLibraryRetargeter.getRetargetedMethod(method, b) != null;
-    }
-  }
-
   @Override
   public void registerInvokeStatic(DexMethod method) {
-    registerLibraryRetargeting(method, false);
     registerInterfaceMethodRewriting(method, STATIC);
     registerDesugaredLibraryAPIConverter(method);
   }
 
   @Override
   public void registerInvokeInterface(DexMethod method) {
-    registerLibraryRetargeting(method, true);
     registerInterfaceMethodRewriting(method, INTERFACE);
     registerDesugaredLibraryAPIConverter(method);
   }
@@ -124,7 +108,6 @@
 
   @Override
   public void registerInvokeSuper(DexMethod method) {
-    registerLibraryRetargeting(method, false);
     registerInterfaceMethodRewriting(method, SUPER);
     registerDesugaredLibraryAPIConverter(method);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 3fb9329..78291d7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -56,5 +56,8 @@
   public abstract <T extends Throwable> void withD8NestBasedAccessDesugaring(
       ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) throws T;
 
+  public abstract void withDesugaredLibraryRetargeter(
+      Consumer<DesugaredLibraryRetargeter> consumer);
+
   public abstract void withRecordRewriter(Consumer<RecordRewriter> consumer);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 6951b39..1c6b337 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.ProgramField;
@@ -42,7 +43,8 @@
         NestBasedAccessDesugaringEventConsumer,
         RecordDesugaringEventConsumer,
         TwrCloseResourceDesugaringEventConsumer,
-        InterfaceMethodDesugaringEventConsumer {
+        InterfaceMethodDesugaringEventConsumer,
+        DesugaredLibraryRetargeterEventConsumer {
 
   public static D8CfInstructionDesugaringEventConsumer createForD8(
       D8MethodProcessor methodProcessor) {
@@ -61,6 +63,21 @@
     return new CfInstructionDesugaringEventConsumer() {
 
       @Override
+      public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+        assert false;
+      }
+
+      @Override
+      public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+        assert false;
+      }
+
+      @Override
+      public void acceptForwardingMethod(ProgramMethod method) {
+        assert false;
+      }
+
+      @Override
       public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
         assert false;
       }
@@ -132,6 +149,21 @@
     }
 
     @Override
+    public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+      methodProcessor.scheduleDesugaredMethodsForProcessing(clazz.programMethods());
+    }
+
+    @Override
+    public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+      // Intentionnaly empty.
+    }
+
+    @Override
+    public void acceptForwardingMethod(ProgramMethod method) {
+      methodProcessor.scheduleDesugaredMethodForProcessing(method);
+    }
+
+    @Override
     public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
       methodProcessor.scheduleMethodForProcessing(backportedMethod, this);
     }
@@ -270,6 +302,24 @@
     }
 
     @Override
+    public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+      // Called only in Desugared library compilation which is D8.
+      assert false;
+    }
+
+    @Override
+    public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+      // TODO(b/188767735): R8 currently relies on IR desugaring.
+      // The classpath class should be marked as liveNonProgramType.
+    }
+
+    @Override
+    public void acceptForwardingMethod(ProgramMethod method) {
+      // TODO(b/188767735): R8 currently relies on IR desugaring.
+      // The method should be marked live, and assert everything it references is traced.
+    }
+
+    @Override
     public void acceptRecordClass(DexProgramClass recordClass) {
       // This is called each time an instruction or a class is found to require the record class.
       assert false : "TODO(b/179146128): To be implemented";
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 68aca31..a3f492c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -4,7 +4,12 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import static com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter.InvokeRetargetingResult.NO_REWRITING;
+
 import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -33,13 +38,13 @@
 import com.android.tools.r8.graph.NestHostClassAttribute;
 import com.android.tools.r8.graph.NestMemberClassAttribute;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
@@ -48,10 +53,9 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.IdentityHashMap;
@@ -61,11 +65,11 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeSet;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
+import java.util.function.Function;
+import org.objectweb.asm.Opcodes;
 
-public class DesugaredLibraryRetargeter {
+public class DesugaredLibraryRetargeter implements CfInstructionDesugaring {
 
   private final AppView<?> appView;
   private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
@@ -76,8 +80,6 @@
   // Non final virtual library methods requiring generation of emulated dispatch.
   private final DexClassAndMethodSet emulatedDispatchMethods = DexClassAndMethodSet.create();
 
-  private final Set<DexProgramClass> programSynthesizedClasses = Sets.newConcurrentHashSet();
-
   public DesugaredLibraryRetargeter(AppView<?> appView) {
     this.appView = appView;
     if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
@@ -215,6 +217,32 @@
     retargetLibraryMember.keySet().forEach(consumer);
   }
 
+  @Override
+  public Collection<CfInstruction> desugarInstruction(
+      CfInstruction instruction,
+      FreshLocalProvider freshLocalProvider,
+      LocalStackAllocator localStackAllocator,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext,
+      DexItemFactory dexItemFactory) {
+    InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context);
+
+    if (!invokeRetargetingResult.hasNewInvokeTarget()) {
+      return null;
+    }
+
+    DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(eventConsumer);
+    return Collections.singletonList(
+        new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, instruction.asInvoke().isInterface()));
+  }
+
+  @Override
+  public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+    return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget();
+  }
+
+  @Deprecated // Use Cf to Cf desugaring instead.
   public void desugar(IRCode code) {
     if (retargetLibraryMember.isEmpty()) {
       return;
@@ -231,68 +259,129 @@
       DexMethod invokedMethod = invoke.getInvokedMethod();
       boolean isInterface = invoke.getInterfaceBit();
 
-      DexMethod retarget = getRetargetedMethod(invokedMethod, isInterface);
-      if (retarget == null) {
-        continue;
+      InvokeRetargetingResult invokeRetargetingResult =
+          computeNewInvokeTarget(
+              invokedMethod, isInterface, invoke.isInvokeSuper(), code.context());
+      if (invokeRetargetingResult.hasNewInvokeTarget()) {
+        DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(null);
+        iterator.replaceCurrentInstruction(
+            new InvokeStatic(newInvokeTarget, invoke.outValue(), invoke.inValues()));
       }
-
-      // Due to emulated dispatch, we have to rewrite invoke-super differently or we end up in
-      // infinite loops. We do direct resolution. This is a very uncommon case.
-      if (invoke.isInvokeSuper() && matchesNonFinalHolderRewrite(invoke.getInvokedMethod())) {
-        DexClassAndMethod superTarget =
-            appView
-                .appInfoForDesugaring()
-                .lookupSuperTarget(invoke.getInvokedMethod(), code.context());
-        // Final methods can be rewritten as a normal invoke.
-        if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
-          DexMethod retargetMethod =
-              appView.options().desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
-          if (retargetMethod != null) {
-            iterator.replaceCurrentInstruction(
-                new InvokeStatic(retargetMethod, invoke.outValue(), invoke.arguments()));
-          }
-          continue;
-        }
-      }
-
-      iterator.replaceCurrentInstruction(
-          new InvokeStatic(retarget, invoke.outValue(), invoke.inValues()));
     }
   }
 
-  public DexMethod getRetargetedMethod(DexMethod invokedMethod, boolean isInterface) {
-    DexMethod retarget = getRetargetLibraryMember(invokedMethod);
-    if (retarget == null) {
+  static class InvokeRetargetingResult {
+
+    static InvokeRetargetingResult NO_REWRITING =
+        new InvokeRetargetingResult(false, ignored -> null);
+
+    private final boolean hasNewInvokeTarget;
+    private final Function<DesugaredLibraryRetargeterEventConsumer, DexMethod>
+        newInvokeTargetSupplier;
+
+    static InvokeRetargetingResult createInvokeRetargetingResult(DexMethod retarget) {
+      if (retarget == null) {
+        return NO_REWRITING;
+      }
+      return new InvokeRetargetingResult(true, ignored -> retarget);
+    }
+
+    private InvokeRetargetingResult(
+        boolean hasNewInvokeTarget,
+        Function<DesugaredLibraryRetargeterEventConsumer, DexMethod> newInvokeTargetSupplier) {
+      this.hasNewInvokeTarget = hasNewInvokeTarget;
+      this.newInvokeTargetSupplier = newInvokeTargetSupplier;
+    }
+
+    public boolean hasNewInvokeTarget() {
+      return hasNewInvokeTarget;
+    }
+
+    public DexMethod getNewInvokeTarget(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
+      assert hasNewInvokeTarget();
+      return newInvokeTargetSupplier.apply(eventConsumer);
+    }
+  }
+
+  public boolean hasNewInvokeTarget(
+      DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
+    return computeNewInvokeTarget(invokedMethod, isInterface, isInvokeSuper, context)
+        .hasNewInvokeTarget();
+  }
+
+  private InvokeRetargetingResult computeNewInvokeTarget(
+      CfInstruction instruction, ProgramMethod context) {
+    if (retargetLibraryMember.isEmpty() || !instruction.isInvoke()) {
+      return NO_REWRITING;
+    }
+    CfInvoke cfInvoke = instruction.asInvoke();
+    return computeNewInvokeTarget(
+        cfInvoke.getMethod(),
+        cfInvoke.isInterface(),
+        cfInvoke.isInvokeSuper(context.getHolderType()),
+        context);
+  }
+
+  private InvokeRetargetingResult computeNewInvokeTarget(
+      DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
+    InvokeRetargetingResult retarget = computeRetargetedMethod(invokedMethod, isInterface);
+    if (!retarget.hasNewInvokeTarget()) {
+      return NO_REWRITING;
+    }
+    if (isInvokeSuper && matchesNonFinalHolderRewrite(invokedMethod)) {
+      DexClassAndMethod superTarget =
+          appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+      // Final methods can be rewritten as a normal invoke.
+      if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
+        return InvokeRetargetingResult.createInvokeRetargetingResult(
+            appView.options().desugaredLibraryConfiguration.retargetMethod(superTarget, appView));
+      }
+    }
+    return retarget;
+  }
+
+  private InvokeRetargetingResult computeRetargetedMethod(
+      DexMethod invokedMethod, boolean isInterface) {
+    InvokeRetargetingResult invokeRetargetingResult = computeRetargetLibraryMember(invokedMethod);
+    if (!invokeRetargetingResult.hasNewInvokeTarget()) {
       if (!matchesNonFinalHolderRewrite(invokedMethod)) {
-        return null;
+        return NO_REWRITING;
       }
       // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
       ResolutionResult resolutionResult =
           appView.appInfoForDesugaring().resolveMethod(invokedMethod, isInterface);
       if (resolutionResult.isFailedResolution()) {
-        return null;
+        return NO_REWRITING;
       }
       DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
       assert singleTarget != null;
-      retarget = getRetargetLibraryMember(singleTarget.getReference());
+      invokeRetargetingResult = computeRetargetLibraryMember(singleTarget.getReference());
     }
-    return retarget;
+    return invokeRetargetingResult;
   }
 
-  private DexMethod getRetargetLibraryMember(DexMethod method) {
+  private InvokeRetargetingResult computeRetargetLibraryMember(DexMethod method) {
     Map<DexType, DexType> backportCoreLibraryMembers =
         appView.options().desugaredLibraryConfiguration.getBackportCoreLibraryMember();
     if (backportCoreLibraryMembers.containsKey(method.holder)) {
       DexType newHolder = backportCoreLibraryMembers.get(method.holder);
-      return appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
+      DexMethod newMethod =
+          appView.dexItemFactory().createMethod(newHolder, method.proto, method.name);
+      return InvokeRetargetingResult.createInvokeRetargetingResult(newMethod);
     }
     DexClassAndMethod emulatedMethod = emulatedDispatchMethods.get(method);
     if (emulatedMethod != null) {
       assert !emulatedMethod.getAccessFlags().isStatic();
-      DexType newHolder = ensureEmulatedHolderDispatchMethod(emulatedMethod).type;
-      return computeRetargetMethod(method, emulatedMethod.getAccessFlags().isStatic(), newHolder);
+      return new InvokeRetargetingResult(
+          true,
+          eventConsumer -> {
+            DexType newHolder =
+                ensureEmulatedHolderDispatchMethod(emulatedMethod, eventConsumer).type;
+            return computeRetargetMethod(
+                method, emulatedMethod.getAccessFlags().isStatic(), newHolder);
+          });
     }
-    return retargetLibraryMember.get(method);
+    return InvokeRetargetingResult.createInvokeRetargetingResult(retargetLibraryMember.get(method));
   }
 
   private boolean matchesNonFinalHolderRewrite(DexMethod method) {
@@ -427,10 +516,8 @@
     }
   }
 
-  public void synthesizeRetargetClasses(ExecutorService executorService, IRConverter converter)
-      throws ExecutionException {
-    new EmulatedDispatchTreeFixer().fixApp(executorService, converter);
-    converter.optimizeSynthesizedClasses(programSynthesizedClasses, executorService);
+  public void finalizeDesugaring(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
+    new EmulatedDispatchTreeFixer().fixApp(eventConsumer);
   }
 
   private void rewriteType(DexType type) {
@@ -441,8 +528,12 @@
     appView.rewritePrefix.rewriteType(type, newType);
   }
 
-  public DexClass ensureEmulatedHolderDispatchMethod(DexClassAndMethod emulatedDispatchMethod) {
-    DexClass interfaceClass = ensureEmulatedInterfaceDispatchMethod(emulatedDispatchMethod);
+  public DexClass ensureEmulatedHolderDispatchMethod(
+      DexClassAndMethod emulatedDispatchMethod,
+      DesugaredLibraryRetargeterEventConsumer eventConsumer) {
+    assert eventConsumer != null || appView.enableWholeProgramOptimizations();
+    DexClass interfaceClass =
+        ensureEmulatedInterfaceDispatchMethod(emulatedDispatchMethod, eventConsumer);
     DexMethod itfMethod =
         interfaceClass.lookupMethod(emulatedDispatchMethod.getReference()).getReference();
     DexClass holderDispatch;
@@ -455,8 +546,12 @@
                   emulatedDispatchMethod.getHolder(),
                   appView,
                   classBuilder ->
-                      buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod));
-      programSynthesizedClasses.add(holderDispatch.asProgramClass());
+                      buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
+                  clazz -> {
+                    if (eventConsumer != null) {
+                      eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
+                    }
+                  });
     } else {
       ClasspathOrLibraryClass context =
           emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
@@ -469,13 +564,21 @@
                   context,
                   appView,
                   classBuilder ->
-                      buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod));
+                      buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
+                  clazz -> {
+                    if (eventConsumer != null) {
+                      eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
+                    }
+                  });
     }
     rewriteType(holderDispatch.type);
     return holderDispatch;
   }
 
-  public DexClass ensureEmulatedInterfaceDispatchMethod(DexClassAndMethod emulatedDispatchMethod) {
+  public DexClass ensureEmulatedInterfaceDispatchMethod(
+      DexClassAndMethod emulatedDispatchMethod,
+      DesugaredLibraryRetargeterEventConsumer eventConsumer) {
+    assert eventConsumer != null || appView.enableWholeProgramOptimizations();
     DexClass interfaceDispatch;
     if (appView.options().isDesugaredLibraryCompilation()) {
       interfaceDispatch =
@@ -486,8 +589,12 @@
                   emulatedDispatchMethod.getHolder(),
                   appView,
                   classBuilder ->
-                      this.buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod));
-      programSynthesizedClasses.add(interfaceDispatch.asProgramClass());
+                      buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
+                  clazz -> {
+                    if (eventConsumer != null) {
+                      eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
+                    }
+                  });
     } else {
       ClasspathOrLibraryClass context =
           emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
@@ -500,7 +607,12 @@
                   context,
                   appView,
                   classBuilder ->
-                      this.buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod));
+                      buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
+                  clazz -> {
+                    if (eventConsumer != null) {
+                      eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
+                    }
+                  });
     }
     rewriteType(interfaceDispatch.type);
     return interfaceDispatch;
@@ -550,32 +662,33 @@
         });
   }
 
+  @Deprecated // Use Cf to Cf desugaring.
+  public void synthesizeRetargetClasses() {
+    new EmulatedDispatchTreeFixer().fixApp(null);
+  }
+
   // The rewrite of virtual calls requires to go through emulate dispatch. This class is responsible
   // for inserting interfaces on library boundaries and forwarding methods in the program, and to
   // synthesize the interfaces and emulated dispatch classes in the desugared library.
   class EmulatedDispatchTreeFixer {
 
-    void fixApp(ExecutorService executorService, IRConverter converter) throws ExecutionException {
+    void fixApp(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
       if (appView.options().isDesugaredLibraryCompilation()) {
-        synthesizeEmulatedDispatchMethods();
+        synthesizeEmulatedDispatchMethods(eventConsumer);
       } else {
-        addInterfacesAndForwardingMethods(executorService, converter);
+        addInterfacesAndForwardingMethods(eventConsumer);
       }
     }
 
     private void addInterfacesAndForwardingMethods(
-        ExecutorService executorService, IRConverter converter) throws ExecutionException {
+        DesugaredLibraryRetargeterEventConsumer eventConsumer) {
       assert !appView.options().isDesugaredLibraryCompilation();
       Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
       for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
         map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
         map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
       }
-      SortedProgramMethodSet addedMethods = SortedProgramMethodSet.create();
       for (DexProgramClass clazz : appView.appInfo().classes()) {
-        if (appView.isAlreadyLibraryDesugared(clazz)) {
-          continue;
-        }
         if (clazz.superType == null) {
           assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
           continue;
@@ -589,13 +702,11 @@
           map.forEach(
               (type, methods) -> {
                 if (inherit(superclass.asLibraryClass(), type, emulatedDispatchMethods)) {
-                  addInterfacesAndForwardingMethods(
-                      clazz, methods, method -> addedMethods.createAndAdd(clazz, method));
+                  addInterfacesAndForwardingMethods(eventConsumer, clazz, methods);
                 }
               });
         }
       }
-      converter.processMethodsConcurrently(addedMethods, executorService);
     }
 
     private boolean inherit(
@@ -619,20 +730,26 @@
     }
 
     private void addInterfacesAndForwardingMethods(
+        DesugaredLibraryRetargeterEventConsumer eventConsumer,
         DexProgramClass clazz,
-        List<DexClassAndMethod> methods,
-        Consumer<DexEncodedMethod> newForwardingMethodsConsumer) {
+        List<DexClassAndMethod> methods) {
       // DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
       // methods.
       // We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
       // applies up to 24.
       for (DexClassAndMethod method : methods) {
-        DexClass dexClass = ensureEmulatedInterfaceDispatchMethod(method);
+        DexClass dexClass = ensureEmulatedInterfaceDispatchMethod(method, eventConsumer);
+        if (clazz.interfaces.contains(dexClass.type)) {
+          // The class has already been desugared.
+          continue;
+        }
         clazz.addExtraInterfaces(Collections.singletonList(new ClassTypeSignature(dexClass.type)));
         if (clazz.lookupVirtualMethod(method.getReference()) == null) {
           DexEncodedMethod newMethod = createForwardingMethod(method, clazz);
           clazz.addVirtualMethod(newMethod);
-          newForwardingMethodsConsumer.accept(newMethod);
+          if (eventConsumer != null) {
+            eventConsumer.acceptForwardingMethod(new ProgramMethod(clazz, newMethod));
+          }
         }
       }
     }
@@ -649,13 +766,14 @@
           target, clazz, forwardMethod, appView.dexItemFactory());
     }
 
-    private void synthesizeEmulatedDispatchMethods() {
+    private void synthesizeEmulatedDispatchMethods(
+        DesugaredLibraryRetargeterEventConsumer eventConsumer) {
       assert appView.options().isDesugaredLibraryCompilation();
       if (emulatedDispatchMethods.isEmpty()) {
         return;
       }
       for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
-        ensureEmulatedHolderDispatchMethod(emulatedDispatchMethod);
+        ensureEmulatedHolderDispatchMethod(emulatedDispatchMethod, eventConsumer);
       }
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java
new file mode 100644
index 0000000..3806aa6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2021, 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;
+
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface DesugaredLibraryRetargeterEventConsumer {
+
+  void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz);
+
+  void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz);
+
+  void acceptForwardingMethod(ProgramMethod method);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index c8b6147..5b25c84 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -58,6 +58,11 @@
   }
 
   @Override
+  public void withDesugaredLibraryRetargeter(Consumer<DesugaredLibraryRetargeter> consumer) {
+    // Intentionally empty.
+  }
+
+  @Override
   public void withRecordRewriter(Consumer<RecordRewriter> consumer) {
     // Intentionally empty.
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 0d4a470..bb345a4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -39,11 +39,20 @@
 
   private final NestBasedAccessDesugaring nestBasedAccessDesugaring;
   private final RecordRewriter recordRewriter;
+  private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
 
   NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
     this.appView = appView;
     this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
     BackportedMethodRewriter backportedMethodRewriter = null;
+    desugaredLibraryRetargeter =
+        appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
+                || appView.enableWholeProgramOptimizations()
+            ? null
+            : new DesugaredLibraryRetargeter(appView);
+    if (desugaredLibraryRetargeter != null) {
+      desugarings.add(desugaredLibraryRetargeter);
+    }
     if (appView.options().enableBackportedMethodRewriting()) {
       backportedMethodRewriter = new BackportedMethodRewriter(appView);
     }
@@ -54,7 +63,9 @@
     // TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
     if (appView.options().isInterfaceMethodDesugaringEnabled()
         && !appView.enableWholeProgramOptimizations()) {
-      desugarings.add(new InterfaceMethodRewriter(appView, backportedMethodRewriter));
+      desugarings.add(
+          new InterfaceMethodRewriter(
+              appView, backportedMethodRewriter, desugaredLibraryRetargeter));
     }
     desugarings.add(new LambdaInstructionDesugaring(appView));
     desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
@@ -80,6 +91,7 @@
       AppView<?> appView, InvokeSpecialToSelfDesugaring invokeSpecialToSelfDesugaring) {
     this.appView = appView;
     this.nestBasedAccessDesugaring = null;
+    this.desugaredLibraryRetargeter = null;
     this.recordRewriter = null;
     desugarings.add(invokeSpecialToSelfDesugaring);
   }
@@ -282,6 +294,13 @@
   }
 
   @Override
+  public void withDesugaredLibraryRetargeter(Consumer<DesugaredLibraryRetargeter> consumer) {
+    if (desugaredLibraryRetargeter != null) {
+      consumer.accept(desugaredLibraryRetargeter);
+    }
+  }
+
+  @Override
   public void withRecordRewriter(Consumer<RecordRewriter> consumer) {
     if (recordRewriter != null) {
       consumer.accept(recordRewriter);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
index 7e029ed..cf86667 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -152,7 +152,8 @@
                             builder.addMethod(
                                 methodBuilder ->
                                     synthesizeEmulatedInterfaceMethod(
-                                        method, emulatedInterface, methodBuilder))));
+                                        method, emulatedInterface, methodBuilder))),
+                ignored -> {});
     emulateInterfaceClass.forEachProgramMethod(synthesizedMethods::add);
     assert emulateInterfaceClass.getType()
         == InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType(
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 679a099..182dc39 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
@@ -59,6 +59,7 @@
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
 import com.android.tools.r8.ir.desugar.FreshLocalProvider;
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
@@ -149,6 +150,7 @@
 
   // This is used to filter out double desugaring on backported methods.
   private final BackportedMethodRewriter backportedMethodRewriter;
+  private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
 
   /** Defines a minor variation in desugaring. */
   public enum Flavor {
@@ -159,10 +161,14 @@
   }
 
   // Constructor for cf to cf desugaring.
-  public InterfaceMethodRewriter(AppView<?> appView, BackportedMethodRewriter rewriter) {
+  public InterfaceMethodRewriter(
+      AppView<?> appView,
+      BackportedMethodRewriter rewriter,
+      DesugaredLibraryRetargeter desugaredLibraryRetargeter) {
     this.appView = appView;
     this.converter = null;
     this.backportedMethodRewriter = rewriter;
+    this.desugaredLibraryRetargeter = desugaredLibraryRetargeter;
     this.options = appView.options();
     this.factory = appView.dexItemFactory();
     this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
@@ -176,6 +182,7 @@
     this.appView = appView;
     this.converter = converter;
     this.backportedMethodRewriter = null;
+    this.desugaredLibraryRetargeter = null;
     this.options = appView.options();
     this.factory = appView.dexItemFactory();
     this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
@@ -281,6 +288,17 @@
     return true;
   }
 
+  private boolean isAlreadyRewritten(
+      DexMethod method, boolean itfBit, boolean isSuper, ProgramMethod context) {
+
+    // In Cf to Cf it is forbidden to desugar twice the same instruction, if the backported
+    // method rewriter or the desugared library retargeter already desugar the instruction, they
+    // take precedence and nothing has to be done here.
+    return (backportedMethodRewriter != null && backportedMethodRewriter.methodIsBackport(method))
+        || (desugaredLibraryRetargeter != null
+            && desugaredLibraryRetargeter.hasNewInvokeTarget(method, itfBit, isSuper, context));
+  }
+
   @Override
   public boolean hasPreciseNeedsDesugaring() {
     return false;
@@ -307,7 +325,11 @@
       }
       if (instruction.isInvoke()) {
         CfInvoke cfInvoke = instruction.asInvoke();
-        if (backportedMethodRewriter.methodIsBackport(cfInvoke.getMethod())) {
+        if (isAlreadyRewritten(
+            cfInvoke.getMethod(),
+            cfInvoke.isInterface(),
+            cfInvoke.isInvokeSuper(context.getHolderType()),
+            context)) {
           continue;
         }
         if (cfInvoke.isInvokeStatic()) {
@@ -378,6 +400,13 @@
   public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
     if (instruction.isInvoke()) {
       CfInvoke cfInvoke = instruction.asInvoke();
+      if (isAlreadyRewritten(
+          cfInvoke.getMethod(),
+          cfInvoke.isInterface(),
+          cfInvoke.isInvokeSuper(context.getHolderType()),
+          context)) {
+        return false;
+      }
       return needsRewriting(cfInvoke.getMethod(), cfInvoke.getInvokeType(context), context);
     }
     return false;
@@ -396,7 +425,11 @@
       return null;
     }
     CfInvoke invoke = instruction.asInvoke();
-    if (backportedMethodRewriter.methodIsBackport(invoke.getMethod())) {
+    if (isAlreadyRewritten(
+        invoke.getMethod(),
+        invoke.isInterface(),
+        invoke.isInvokeSuper(context.getHolderType()),
+        context)) {
       return null;
     }
 
@@ -453,11 +486,11 @@
       CfInstructionDesugaringEventConsumer eventConsumer,
       ProgramMethod context,
       MethodProcessingContext methodProcessingContext) {
-    if (backportedMethodRewriter != null
-        && backportedMethodRewriter.methodIsBackport(invoke.getMethod())) {
-      // In Cf to Cf it is not allowed to desugar twice the same instruction, if the backported
-      // method rewriter already desugars the instruction, it takes precedence and nothing has
-      // to be done here.
+    if (isAlreadyRewritten(
+        invoke.getMethod(),
+        invoke.isInterface(),
+        invoke.isInvokeSuper(context.getHolderType()),
+        context)) {
       return null;
     }
 
@@ -769,11 +802,7 @@
           // to outline again the invoke-static. Just do nothing instead.
           return null;
         }
-        if (backportedMethodRewriter != null
-            && backportedMethodRewriter.methodIsBackport(invokedMethod)) {
-          // In Cf to Cf it is not allowed to desugar twice the same instruction, if the backported
-          // method rewriter already desugars the instruction, it takes precedence and nothing has
-          // to be done here.
+        if (isAlreadyRewritten(invokedMethod, interfaceBit, false, context)) {
           return null;
         }
         ProgramMethod newProgramMethod =
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 7675a2c..007821c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -494,7 +494,10 @@
       Consumer<SyntheticProgramClassBuilder> fn) {
     // Obtain the outer synthesizing context in the case the context itself is synthetic.
     // This is to ensure a flat input-type -> synthetic-item mapping.
-    SynthesizingContext outerContext = getSynthesizingContext(context, appView);
+    SynthesizingContext outerContext =
+        context.isProgramClass()
+            ? getSynthesizingContext(context.asProgramClass(), appView)
+            : SynthesizingContext.fromNonSyntheticInputContext(context.asClasspathOrLibraryClass());
     DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
     return internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory());
   }
@@ -508,7 +511,8 @@
       SyntheticKind kind,
       DexClass context,
       AppView<?> appView,
-      Consumer<SyntheticProgramClassBuilder> fn) {
+      Consumer<SyntheticProgramClassBuilder> fn,
+      Consumer<DexProgramClass> onCreationConsumer) {
     assert kind.isFixedSuffixSynthetic;
     // Obtain the outer synthesizing context in the case the context itself is synthetic.
     // This is to ensure a flat input-type -> synthetic-item mapping.
@@ -534,7 +538,10 @@
         return clazz.asProgramClass();
       }
       assert !isSyntheticClass(type);
-      return internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory());
+      DexProgramClass dexProgramClass =
+          internalCreateClass(kind, fn, outerContext, type, appView.dexItemFactory());
+      onCreationConsumer.accept(dexProgramClass);
+      return dexProgramClass;
     }
   }
 
@@ -582,7 +589,8 @@
       SyntheticKind kind,
       ClasspathOrLibraryClass context,
       AppView<?> appView,
-      Consumer<SyntheticClasspathClassBuilder> classConsumer) {
+      Consumer<SyntheticClasspathClassBuilder> classConsumer,
+      Consumer<DexClasspathClass> onCreationConsumer) {
     SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
     DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
     DexClass dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type);
@@ -602,6 +610,7 @@
           new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory());
       classConsumer.accept(classBuilder);
       DexClasspathClass clazz = classBuilder.build();
+      onCreationConsumer.accept(clazz);
       addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
       return clazz;
     }