Merge commit '483aabff576b08c1211b09e32c7e682eb4f59749' into dev-release
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 6fea9a2..ec9955d 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -160,7 +160,6 @@
     assert !internal.minimalMainDex;
     internal.minApiLevel = getMinApiLevel();
     assert !internal.intermediate;
-    internal.intermediate = true;
     assert internal.readCompileTimeAnnotations;
     internal.programConsumer = getProgramConsumer();
     assert internal.programConsumer instanceof ClassFileConsumer;
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index f2959bb..d1e6951 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -366,11 +366,15 @@
             new DexValueString(dexItemFactory.createString(kind.descriptor)));
     DexAnnotationElement typeElement =
         new DexAnnotationElement(dexItemFactory.valueString, new DexValueType(synthesizingContext));
+    DexAnnotationElement[] elements;
+    if (synthesizingContext == null) {
+      elements = new DexAnnotationElement[] {kindElement};
+    } else {
+      elements = new DexAnnotationElement[] {kindElement, typeElement};
+    }
     return new DexAnnotation(
         VISIBILITY_BUILD,
-        new DexEncodedAnnotation(
-            dexItemFactory.annotationSynthesizedClass,
-            new DexAnnotationElement[] {kindElement, typeElement}));
+        new DexEncodedAnnotation(dexItemFactory.annotationSynthesizedClass, elements));
   }
 
   public static boolean hasSynthesizedClassAnnotation(
@@ -387,7 +391,8 @@
     if (annotation.annotation.type != factory.annotationSynthesizedClass) {
       return null;
     }
-    if (annotation.annotation.elements.length != 2) {
+    int length = annotation.annotation.elements.length;
+    if (length != 1 && length != 2) {
       return null;
     }
     assert factory.kindString.isLessThan(factory.valueString);
@@ -404,6 +409,9 @@
     if (kind == null) {
       return null;
     }
+    if (length != 2) {
+      return new Pair<>(kind, null);
+    }
     DexAnnotationElement valueElement = annotation.annotation.elements[1];
     if (valueElement.name != factory.valueString) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 60c0ec6..04cf5cc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -61,7 +61,6 @@
 import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
-import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.FieldAccessorBuilder;
 import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
@@ -78,7 +77,6 @@
 import com.android.tools.r8.utils.ConsumerUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OptionalBool;
-import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.Ordered;
@@ -1254,27 +1252,6 @@
     return builder.build();
   }
 
-  public static DexEncodedMethod toEmulateDispatchLibraryMethod(
-      DexType interfaceType,
-      DexMethod newMethod,
-      DexMethod companionMethod,
-      DexMethod libraryMethod,
-      List<Pair<DexType, DexMethod>> extraDispatchCases,
-      AppView<?> appView) {
-    CfCode code =
-        new EmulateInterfaceSyntheticCfCodeProvider(
-                interfaceType, companionMethod, libraryMethod, extraDispatchCases, appView)
-            .generateCfCode();
-    return new DexEncodedMethod(
-        newMethod,
-        MethodAccessFlags.createPublicStaticSynthetic(),
-        MethodTypeSignature.noSignature(),
-        DexAnnotationSet.empty(),
-        ParameterAnnotationsList.empty(),
-        code,
-        true);
-  }
-
   public ProgramMethod toStaticForwardingBridge(
       DexProgramClass holder, DexMethod newMethod, DexItemFactory dexItemFactory) {
     assert isPrivate()
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index cb810de..7a13785 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -275,7 +275,8 @@
             group.getClassIdField(),
             superMethod,
             newMethodReference,
-            bridgeMethodReference);
+            bridgeMethodReference,
+            appView.dexItemFactory());
     DexEncodedMethod newMethod =
         new DexEncodedMethod(
             newMethodReference,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
index 8c649b0..da12b74 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
@@ -31,7 +31,7 @@
 
   @Override
   public SourceCodeProvider getSourceCodeProvider() {
-    return callerPosition ->
+    return (ignored, callerPosition) ->
         new ConstructorEntryPoint(
             typeConstructors, newConstructor, classIdField, callerPosition, originalMethod);
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
index 3995fd8..4402a83 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
@@ -5,7 +5,9 @@
 package com.android.tools.r8.horizontalclassmerging.code;
 
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.horizontalclassmerging.VirtualMethodEntryPoint;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
@@ -15,20 +17,38 @@
 public class VirtualMethodEntryPointSynthesizedCode extends SynthesizedCode {
   private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
 
+  private final DexItemFactory factory;
+
   public VirtualMethodEntryPointSynthesizedCode(
       Int2ReferenceSortedMap<DexMethod> mappedMethods,
       DexField classIdField,
       DexMethod superMethod,
       DexMethod method,
-      DexMethod originalMethod) {
+      DexMethod originalMethod,
+      DexItemFactory factory) {
     super(
-        position ->
+        (context, position) ->
             new VirtualMethodEntryPoint(
-                mappedMethods, classIdField, superMethod, method, position, originalMethod));
-
+                mappedMethods,
+                classIdField,
+                computeSuperMethodTarget(superMethod, context, factory),
+                method,
+                position,
+                originalMethod));
+    this.factory = factory;
     this.mappedMethods = mappedMethods;
   }
 
+  private static DexMethod computeSuperMethodTarget(
+      DexMethod superMethod, ProgramMethod method, DexItemFactory factory) {
+    // We are only using superMethod as a bit but if this is changed to generate CfCode directly,
+    // the superMethod needs to be computed by mapping through the lens.
+    if (superMethod == null) {
+      return null;
+    }
+    return method.getReference().withHolder(method.getHolder().superType, factory);
+  }
+
   @Override
   public Consumer<UseRegistry> getRegistryCallback() {
     return this::registerReachableDefinitions;
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 4f4673a..5a96760 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
@@ -8,6 +8,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -42,6 +43,7 @@
 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.utils.InternalOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -677,13 +679,22 @@
           appView
               .dexItemFactory()
               .createMethod(dispatchHolder, desugarMethod.proto, emulatedDispatchMethod.getName());
-      return DexEncodedMethod.toEmulateDispatchLibraryMethod(
-          emulatedDispatchMethod.getHolderType(),
+      CfCode code =
+          new EmulateInterfaceSyntheticCfCodeProvider(
+                  emulatedDispatchMethod.getHolderType(),
+                  desugarMethod,
+                  itfMethod,
+                  Collections.emptyList(),
+                  appView)
+              .generateCfCode();
+      return new DexEncodedMethod(
           newMethod,
-          desugarMethod,
-          itfMethod,
-          Collections.emptyList(),
-          appView);
+          MethodAccessFlags.createPublicStaticSynthetic(),
+          MethodTypeSignature.noSignature(),
+          DexAnnotationSet.empty(),
+          ParameterAnnotationsList.empty(),
+          code,
+          true);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 956cd2c..4c784f0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -765,7 +765,7 @@
             DexAnnotationSet.empty(),
             ParameterAnnotationsList.empty(),
             new SynthesizedCode(
-                callerPosition ->
+                (ignored, callerPosition) ->
                     new ExceptionThrowingSourceCode(clazz.type, method, callerPosition, errorType)),
             true);
     addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
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 e8dc257..ff28c27 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
@@ -5,26 +5,28 @@
 
 import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.emulateInterfaceLibraryMethod;
 
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.GenericSignature;
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
+import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
+import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -40,8 +42,6 @@
   private final InterfaceMethodRewriter rewriter;
   private final Map<DexType, DexType> emulatedInterfaces;
   private final Map<DexType, List<DexType>> emulatedInterfacesHierarchy;
-  // All created emulated interface classes indexed by emulated interface type.
-  final Map<DexProgramClass, DexProgramClass> syntheticClasses = new IdentityHashMap<>();
 
   EmulatedInterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
     this.appView = appView;
@@ -136,128 +136,118 @@
     return newMethods;
   }
 
-  void generateEmulateInterfaceLibrary(DexProgramClass emulatedInterface) {
+  DexProgramClass ensureEmulateInterfaceLibrary(
+      DexProgramClass emulatedInterface, ProgramMethodSet synthesizedMethods) {
     assert rewriter.isEmulatedInterface(emulatedInterface.type);
-    DexProgramClass theProgramInterface = emulatedInterface.asProgramClass();
-    DexProgramClass synthesizedClass = synthesizeEmulateInterfaceLibraryClass(theProgramInterface);
-    if (synthesizedClass != null) {
-      syntheticClasses.put(emulatedInterface, synthesizedClass);
-    }
-  }
-
-  private DexProgramClass synthesizeEmulateInterfaceLibraryClass(DexProgramClass theInterface) {
-    List<DexEncodedMethod> emulationMethods = new ArrayList<>();
-    theInterface.forEachProgramMethodMatching(
-        DexEncodedMethod::isDefaultMethod,
-        method -> {
-          DexMethod libraryMethod =
-              method
-                  .getReference()
-                  .withHolder(emulatedInterfaces.get(theInterface.type), appView.dexItemFactory());
-          DexMethod companionMethod =
-              method.getAccessFlags().isStatic()
-                  ? rewriter.staticAsMethodOfCompanionClass(method)
-                  : rewriter.defaultAsMethodOfCompanionClass(method);
-          // To properly emulate the library interface call, we need to compute the interfaces
-          // inheriting from the interface and manually implement the dispatch with instance of.
-          // The list guarantees that an interface is always after interfaces it extends,
-          // hence reverse iteration.
-          List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
-          List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
-          // In practice, there is usually a single case (except for tests),
-          // so we do not bother to make the following loop more clever.
-          Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
-              appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
-          for (DexString methodName : retargetCoreLibMember.keySet()) {
-            if (method.getName() == methodName) {
-              for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
-                DexClass inClass = appView.definitionFor(inType);
-                if (inClass != null && implementsInterface(inClass, theInterface.type)) {
-                  extraDispatchCases.add(
-                      new Pair<>(
-                          inType,
-                          appView
-                              .dexItemFactory()
-                              .createMethod(
-                                  retargetCoreLibMember.get(methodName).get(inType),
-                                  appView
-                                      .dexItemFactory()
-                                      .protoWithDifferentFirstParameter(
-                                          companionMethod.proto, inType),
-                                  method.getName())));
-                }
-              }
-            }
-          }
-          if (subInterfaces != null) {
-            for (int i = subInterfaces.size() - 1; i >= 0; i--) {
-              DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
-              assert subInterfaceClass != null;
-              // Else computation of subInterface would have failed.
-              // if the method is implemented, extra dispatch is required.
-              DexEncodedMethod result =
-                  subInterfaceClass.lookupVirtualMethod(method.getReference());
-              if (result != null && !result.isAbstract()) {
-                extraDispatchCases.add(
-                    new Pair<>(
-                        subInterfaceClass.type,
-                        appView
-                            .dexItemFactory()
-                            .createMethod(
-                                rewriter.getCompanionClassType(subInterfaceClass.type),
-                                appView
-                                    .dexItemFactory()
-                                    .protoWithDifferentFirstParameter(
-                                        companionMethod.proto, subInterfaceClass.type),
-                                companionMethod.name)));
-              }
-            }
-          }
-          emulationMethods.add(
-              DexEncodedMethod.toEmulateDispatchLibraryMethod(
-                  method.getHolderType(),
-                  emulateInterfaceLibraryMethod(method, rewriter.factory),
-                  companionMethod,
-                  libraryMethod,
-                  extraDispatchCases,
-                  appView));
-        });
-    if (emulationMethods.isEmpty()) {
-      return null;
-    }
     DexType emulateLibraryClassType =
         InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType(
-            theInterface.type, appView.dexItemFactory());
-    ClassAccessFlags emulateLibraryClassFlags = theInterface.accessFlags.copy();
-    emulateLibraryClassFlags.unsetAbstract();
-    emulateLibraryClassFlags.unsetInterface();
-    emulateLibraryClassFlags.unsetAnnotation();
-    emulateLibraryClassFlags.setFinal();
-    emulateLibraryClassFlags.setSynthetic();
-    emulateLibraryClassFlags.setPublic();
-    DexProgramClass clazz =
-        new DexProgramClass(
-            emulateLibraryClassType,
-            null,
-            new SynthesizedOrigin("interface desugaring (libs)", getClass()),
-            emulateLibraryClassFlags,
-            appView.dexItemFactory().objectType,
-            DexTypeList.empty(),
-            theInterface.sourceFile,
-            null,
-            Collections.emptyList(),
-            null,
-            Collections.emptyList(),
-            ClassSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            DexEncodedField.EMPTY_ARRAY,
-            DexEncodedField.EMPTY_ARRAY,
-            // All synthesized methods are static in this case.
-            emulationMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
-            DexEncodedMethod.EMPTY_ARRAY,
-            appView.dexItemFactory().getSkipNameValidationForTesting(),
-            DexProgramClass::checksumFromType);
-    return clazz;
+            emulatedInterface.type, appView.dexItemFactory());
+    DexProgramClass emulateInterfaceClass =
+        appView
+            .getSyntheticItems()
+            .ensureFixedClassWhileMigrating(
+                SyntheticNaming.SyntheticKind.EMULATED_INTERFACE_CLASS,
+                emulateLibraryClassType,
+                emulatedInterface,
+                appView,
+                builder ->
+                    emulatedInterface.forEachProgramMethodMatching(
+                        DexEncodedMethod::isDefaultMethod,
+                        method ->
+                            builder.addMethod(
+                                methodBuilder ->
+                                    synthesizeEmulatedInterfaceMethod(
+                                        method, emulatedInterface, methodBuilder))));
+    emulateInterfaceClass.forEachProgramMethod(synthesizedMethods::add);
+    return emulateInterfaceClass;
+  }
+
+  private void synthesizeEmulatedInterfaceMethod(
+      ProgramMethod method, DexProgramClass theInterface, SyntheticMethodBuilder methodBuilder) {
+    DexMethod libraryMethod =
+        method
+            .getReference()
+            .withHolder(emulatedInterfaces.get(theInterface.type), appView.dexItemFactory());
+    DexMethod companionMethod =
+        method.getAccessFlags().isStatic()
+            ? rewriter.staticAsMethodOfCompanionClass(method)
+            : rewriter.defaultAsMethodOfCompanionClass(method);
+    List<Pair<DexType, DexMethod>> extraDispatchCases =
+        getDispatchCases(method, theInterface, companionMethod);
+    DexMethod emulatedMethod = emulateInterfaceLibraryMethod(method, appView.dexItemFactory());
+    methodBuilder
+        .setName(emulatedMethod.getName())
+        .setProto(emulatedMethod.getProto())
+        .setCode(
+            theMethod ->
+                new EmulateInterfaceSyntheticCfCodeProvider(
+                        theMethod.getHolderType(),
+                        companionMethod,
+                        libraryMethod,
+                        extraDispatchCases,
+                        appView)
+                    .generateCfCode())
+        .setAccessFlags(
+            MethodAccessFlags.fromSharedAccessFlags(
+                Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false));
+  }
+
+  private List<Pair<DexType, DexMethod>> getDispatchCases(
+      ProgramMethod method, DexProgramClass theInterface, DexMethod companionMethod) {
+    // To properly emulate the library interface call, we need to compute the interfaces
+    // inheriting from the interface and manually implement the dispatch with instance of.
+    // The list guarantees that an interface is always after interfaces it extends,
+    // hence reverse iteration.
+    List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
+    List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
+    // In practice, there is usually a single case (except for tests),
+    // so we do not bother to make the following loop more clever.
+    Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+        appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+    for (DexString methodName : retargetCoreLibMember.keySet()) {
+      if (method.getName() == methodName) {
+        for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+          DexClass inClass = appView.definitionFor(inType);
+          if (inClass != null && implementsInterface(inClass, theInterface.type)) {
+            extraDispatchCases.add(
+                new Pair<>(
+                    inType,
+                    appView
+                        .dexItemFactory()
+                        .createMethod(
+                            retargetCoreLibMember.get(methodName).get(inType),
+                            appView
+                                .dexItemFactory()
+                                .protoWithDifferentFirstParameter(companionMethod.proto, inType),
+                            method.getName())));
+          }
+        }
+      }
+    }
+    if (subInterfaces != null) {
+      for (int i = subInterfaces.size() - 1; i >= 0; i--) {
+        DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
+        assert subInterfaceClass != null;
+        // Else computation of subInterface would have failed.
+        // if the method is implemented, extra dispatch is required.
+        DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference());
+        if (result != null && !result.isAbstract()) {
+          extraDispatchCases.add(
+              new Pair<>(
+                  subInterfaceClass.type,
+                  appView
+                      .dexItemFactory()
+                      .createMethod(
+                          rewriter.getCompanionClassType(subInterfaceClass.type),
+                          appView
+                              .dexItemFactory()
+                              .protoWithDifferentFirstParameter(
+                                  companionMethod.proto, subInterfaceClass.type),
+                          companionMethod.name)));
+        }
+      }
+    }
+    return extraDispatchCases;
   }
 
   private boolean implementsInterface(DexClass clazz, DexType interfaceType) {
@@ -282,14 +272,19 @@
         || appView.isAlreadyLibraryDesugared(emulatedInterface)) {
       return;
     }
-    generateEmulateInterfaceLibrary(emulatedInterface);
+    if (needsEmulateInterfaceLibrary(emulatedInterface)) {
+      ensureEmulateInterfaceLibrary(emulatedInterface, synthesizedMethods);
+    }
+  }
+
+  private boolean needsEmulateInterfaceLibrary(DexProgramClass emulatedInterface) {
+    return Iterables.any(emulatedInterface.methods(), DexEncodedMethod::isDefaultMethod);
   }
 
   @Override
   public void finalizeProcessing(Builder<?> builder, ProgramMethodSet synthesizedMethods) {
     warnMissingEmulatedInterfaces();
     if (!appView.options().isDesugaredLibraryCompilation()) {
-      assert syntheticClasses.isEmpty();
       return;
     }
     for (DexType interfaceType : emulatedInterfaces.keySet()) {
@@ -302,12 +297,6 @@
         }
       }
     }
-    syntheticClasses.forEach(
-        (interfaceClass, synthesizedClass) -> {
-          builder.addSynthesizedClass(synthesizedClass);
-          appView.appInfo().addSynthesizedClass(synthesizedClass, interfaceClass);
-          synthesizedClass.forEachProgramMethod(synthesizedMethods::add);
-        });
     // TODO(b/183918843): Investigate what to do for the filtering, the minimum would be to make
     // the rewriting rule explicit instead of using the synthesized class prefix.
     filterEmulatedInterfaceSubInterfaces(builder);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index e847977..b0df7c7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication.Builder;
@@ -28,21 +27,18 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodCollection;
 import com.android.tools.r8.graph.NestedGraphLens;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
+import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
@@ -53,7 +49,6 @@
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
@@ -76,9 +71,6 @@
   private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos =
       new ConcurrentHashMap<>();
 
-  // All created companion classes indexed by interface type.
-  final Map<DexClass, DexProgramClass> syntheticClasses = new ConcurrentHashMap<>();
-
   InterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
     this.appView = appView;
     this.rewriter = rewriter;
@@ -91,7 +83,7 @@
     }
     analyzeBridges(iface);
     if (needsCompanionClass(iface)) {
-      synthesizeCompanionClass(iface, synthesizedMethods);
+      ensureCompanionClass(iface, synthesizedMethods);
     }
   }
 
@@ -124,72 +116,54 @@
     return false;
   }
 
-  private void synthesizeCompanionClass(
+  private DexProgramClass ensureCompanionClass(
       DexProgramClass iface, ProgramMethodSet synthesizedMethods) {
-    // The list of methods to be created in companion class.
-    List<DexEncodedMethod> companionMethods = new ArrayList<>();
 
-    ensureCompanionClassInitializesInterface(iface, companionMethods);
-
-    // Process virtual interface methods first.
-    processVirtualInterfaceMethods(iface, companionMethods);
-
-    // Process static and private methods, move them into companion class as well,
-    // make private instance methods public static.
-    processDirectInterfaceMethods(iface, companionMethods);
-
-    assert !companionMethods.isEmpty();
-
-    ClassAccessFlags companionClassFlags = iface.accessFlags.copy();
-    companionClassFlags.unsetAbstract();
-    companionClassFlags.unsetInterface();
-    companionClassFlags.unsetAnnotation();
-    companionClassFlags.setFinal();
-    companionClassFlags.setSynthetic();
-    // Companion class must be public so moved methods can be called from anywhere.
-    companionClassFlags.setPublic();
-
-    // Create companion class.
-    DexType companionClassType = rewriter.getCompanionClassType(iface.type);
     DexProgramClass companionClass =
-        new DexProgramClass(
-            companionClassType,
-            null,
-            new SynthesizedOrigin("interface desugaring", getClass()),
-            companionClassFlags,
-            rewriter.factory.objectType,
-            DexTypeList.empty(),
-            iface.sourceFile,
-            null,
-            Collections.emptyList(),
-            null,
-            Collections.emptyList(),
-            ClassSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            DexEncodedField.EMPTY_ARRAY,
-            DexEncodedField.EMPTY_ARRAY,
-            companionMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
-            DexEncodedMethod.EMPTY_ARRAY,
-            rewriter.factory.getSkipNameValidationForTesting(),
-            getChecksumSupplier(iface));
-    syntheticClasses.put(iface, companionClass);
+        appView
+            .getSyntheticItems()
+            .ensureFixedClassWhileMigrating(
+                SyntheticNaming.SyntheticKind.COMPANION_CLASS,
+                rewriter.getCompanionClassType(iface.type),
+                iface,
+                appView,
+                builder -> {
+                  builder.setSourceFile(iface.sourceFile);
+                  ensureCompanionClassInitializesInterface(iface, builder);
+                  processVirtualInterfaceMethods(iface, builder);
+                  processDirectInterfaceMethods(iface, builder);
+                });
+
+    assert companionClass.hasMethods();
+
+    // D8 and R8 don't need to optimize the methods since they are just moved from interfaces and
+    // don't need to be re-processed, besides the clinit, which has just been inserted.
     if (companionClass.hasClassInitializer()) {
       synthesizedMethods.add(companionClass.getProgramClassInitializer());
     }
+
+    return companionClass;
   }
 
   private void ensureCompanionClassInitializesInterface(
-      DexProgramClass iface, List<DexEncodedMethod> companionMethods) {
+      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
     if (!hasStaticMethodThatTriggersNonTrivialClassInitializer(iface)) {
       return;
     }
+    DexEncodedField clinitField = ensureStaticClinitFieldToTriggerinterfaceInitialization(iface);
+    builder.addMethod(
+        methodBuilder -> createCompanionClassInitializer(iface, clinitField, methodBuilder));
+  }
+
+  private DexEncodedField ensureStaticClinitFieldToTriggerinterfaceInitialization(
+      DexProgramClass iface) {
     DexEncodedField clinitField =
         findExistingStaticClinitFieldToTriggerInterfaceInitialization(iface);
     if (clinitField == null) {
       clinitField = createStaticClinitFieldToTriggerInterfaceInitialization(iface);
       iface.appendStaticField(clinitField);
     }
-    companionMethods.add(createCompanionClassInitializer(iface, clinitField));
+    return clinitField;
   }
 
   private boolean hasStaticMethodThatTriggersNonTrivialClassInitializer(DexProgramClass iface) {
@@ -226,37 +200,34 @@
         DexValueInt.DEFAULT);
   }
 
-  private DexEncodedMethod createCompanionClassInitializer(
-      DexProgramClass iface, DexEncodedField clinitField) {
-    DexType companionType = rewriter.getCompanionClassType(iface.getType());
-    DexMethod clinitMethodReference = appView.dexItemFactory().createClinitMethod(companionType);
-    CfCode code =
-        new CfCode(
-            companionType,
-            clinitField.getType().isWideType() ? 2 : 1,
-            0,
-            ImmutableList.of(
-                new CfFieldInstruction(
-                    Opcodes.GETSTATIC, clinitField.getReference(), clinitField.getReference()),
-                clinitField.getType().isWideType()
-                    ? new CfStackInstruction(Opcode.Pop2)
-                    : new CfStackInstruction(Opcode.Pop),
-                new CfReturnVoid()),
-            ImmutableList.of(),
-            ImmutableList.of());
-    return new DexEncodedMethod(
-        clinitMethodReference,
-        MethodAccessFlags.builder().setConstructor().setPackagePrivate().setStatic().build(),
-        MethodTypeSignature.noSignature(),
-        DexAnnotationSet.empty(),
-        ParameterAnnotationsList.empty(),
-        code,
-        true,
-        iface.getInitialClassFileVersion());
+  private void createCompanionClassInitializer(
+      DexProgramClass iface, DexEncodedField clinitField, SyntheticMethodBuilder methodBuilder) {
+    SyntheticMethodBuilder.SyntheticCodeGenerator codeGenerator =
+        method ->
+            new CfCode(
+                method.holder,
+                clinitField.getType().isWideType() ? 2 : 1,
+                0,
+                ImmutableList.of(
+                    new CfFieldInstruction(
+                        Opcodes.GETSTATIC, clinitField.getReference(), clinitField.getReference()),
+                    clinitField.getType().isWideType()
+                        ? new CfStackInstruction(Opcode.Pop2)
+                        : new CfStackInstruction(Opcode.Pop),
+                    new CfReturnVoid()),
+                ImmutableList.of(),
+                ImmutableList.of());
+    methodBuilder
+        .setName(appView.dexItemFactory().classConstructorMethodName)
+        .setProto(appView.dexItemFactory().createProto(appView.dexItemFactory().voidType))
+        .setAccessFlags(
+            MethodAccessFlags.builder().setConstructor().setPackagePrivate().setStatic().build())
+        .setCode(codeGenerator)
+        .setClassFileVersion(iface.getInitialClassFileVersion());
   }
 
   private void processVirtualInterfaceMethods(
-      DexProgramClass iface, List<DexEncodedMethod> companionMethods) {
+      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
     for (ProgramMethod method : iface.virtualProgramMethods()) {
       DexEncodedMethod virtual = method.getDefinition();
       if (rewriter.isDefaultMethod(virtual)) {
@@ -282,25 +253,29 @@
         newFlags.promoteToStatic();
         DexEncodedMethod.setDebugInfoWithFakeThisParameter(
             code, companionMethod.getArity(), appView);
-        DexEncodedMethod implMethod =
-            new DexEncodedMethod(
-                companionMethod,
-                newFlags,
-                virtual.getGenericSignature(),
-                virtual.annotations(),
-                virtual.parameterAnnotationsList,
-                code,
-                true);
-        implMethod.copyMetadata(virtual);
-        companionMethods.add(implMethod);
-        getPostProcessingInterfaceInfo(iface)
-            .mapDefaultMethodToCompanionMethod(virtual, implMethod);
+
+        builder.addMethod(
+            methodBuilder ->
+                methodBuilder
+                    .setName(companionMethod.getName())
+                    .setProto(companionMethod.getProto())
+                    .setAccessFlags(newFlags)
+                    .setGenericSignature(virtual.getGenericSignature())
+                    .setAnnotations(virtual.annotations())
+                    .setParameterAnnotationsList(virtual.getParameterAnnotations())
+                    .setCode(ignored -> virtual.getCode())
+                    .setOnBuildConsumer(
+                        implMethod -> {
+                          implMethod.copyMetadata(virtual);
+                          getPostProcessingInterfaceInfo(iface)
+                              .mapDefaultMethodToCompanionMethod(virtual, implMethod);
+                        }));
       }
     }
   }
 
   private void processDirectInterfaceMethods(
-      DexProgramClass iface, List<DexEncodedMethod> companionMethods) {
+      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
     for (ProgramMethod method : iface.directProgramMethods()) {
       DexEncodedMethod definition = method.getDefinition();
       if (definition.isClassInitializer()) {
@@ -330,17 +305,22 @@
                 + "either be public or private in "
                 + iface.origin;
         DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(method);
-        DexEncodedMethod implMethod =
-            new DexEncodedMethod(
-                companionMethod,
-                newFlags,
-                definition.getGenericSignature(),
-                definition.annotations(),
-                definition.parameterAnnotationsList,
-                definition.getCode(),
-                true);
-        implMethod.copyMetadata(definition);
-        companionMethods.add(implMethod);
+
+        builder.addMethod(
+            methodBuilder ->
+                methodBuilder
+                    .setName(companionMethod.getName())
+                    .setProto(companionMethod.getProto())
+                    .setAccessFlags(newFlags)
+                    .setGenericSignature(definition.getGenericSignature())
+                    .setAnnotations(definition.annotations())
+                    .setParameterAnnotationsList(definition.getParameterAnnotations())
+                    .setCode(ignored -> definition.getCode())
+                    .setOnBuildConsumer(
+                        implMethod -> {
+                          implMethod.copyMetadata(definition);
+                        }));
+
         getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companionMethod);
         continue;
       }
@@ -361,19 +341,25 @@
                 + oldMethod.toSourceString(),
             iface.origin);
       }
+
       DexEncodedMethod.setDebugInfoWithFakeThisParameter(code, companionMethod.getArity(), appView);
-      DexEncodedMethod implMethod =
-          new DexEncodedMethod(
-              companionMethod,
-              newFlags,
-              definition.getGenericSignature(),
-              definition.annotations(),
-              definition.parameterAnnotationsList,
-              code,
-              true);
-      implMethod.copyMetadata(definition);
-      companionMethods.add(implMethod);
-      getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companionMethod);
+
+      builder.addMethod(
+          methodBuilder ->
+              methodBuilder
+                  .setName(companionMethod.getName())
+                  .setProto(companionMethod.getProto())
+                  .setAccessFlags(newFlags)
+                  .setGenericSignature(definition.getGenericSignature())
+                  .setAnnotations(definition.annotations())
+                  .setParameterAnnotationsList(definition.getParameterAnnotations())
+                  .setCode(ignored -> definition.getCode())
+                  .setOnBuildConsumer(
+                      implMethod -> {
+                        implMethod.copyMetadata(definition);
+                        getPostProcessingInterfaceInfo(iface)
+                            .moveMethod(oldMethod, companionMethod);
+                      }));
     }
   }
 
@@ -387,14 +373,6 @@
     }
   }
 
-  private ChecksumSupplier getChecksumSupplier(DexProgramClass iface) {
-    if (!appView.options().encodeChecksums) {
-      return DexProgramClass::invalidChecksumRequest;
-    }
-    long checksum = iface.getChecksum();
-    return c -> 7 * checksum;
-  }
-
   private boolean canMoveToCompanionClass(DexEncodedMethod method) {
     Code code = method.getCode();
     assert code != null;
@@ -525,13 +503,6 @@
     if (appView.enableWholeProgramOptimizations() && graphLens != null) {
       appView.setGraphLens(graphLens);
     }
-    syntheticClasses.forEach(
-        (interfaceClass, synthesizedClass) -> {
-          // Don't need to optimize synthesized class since all of its methods
-          // are just moved from interfaces and don't need to be re-processed.
-          builder.addSynthesizedClass(synthesizedClass);
-          appView.appInfo().addSynthesizedClass(synthesizedClass, interfaceClass.asProgramClass());
-        });
     new InterfaceMethodRewriterFixup(appView, graphLens).run();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
index ad43a6c..4dc56de 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
@@ -25,7 +25,7 @@
 public abstract class AbstractSynthesizedCode extends Code {
 
   public interface SourceCodeProvider {
-    SourceCode get(Position callerPosition);
+    SourceCode get(ProgramMethod context, Position callerPosition);
   }
 
   public abstract SourceCodeProvider getSourceCodeProvider();
@@ -39,7 +39,7 @@
 
   @Override
   public final IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
-    return IRBuilder.create(method, appView, getSourceCodeProvider().get(null), origin)
+    return IRBuilder.create(method, appView, getSourceCodeProvider().get(method, null), origin)
         .build(method);
   }
 
@@ -55,7 +55,7 @@
     return IRBuilder.createForInlining(
             method,
             appView,
-            getSourceCodeProvider().get(callerPosition),
+            getSourceCodeProvider().get(context, callerPosition),
             origin,
             methodProcessor,
             valueNumberGenerator)
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
index e5a79bb..9e708cc 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.Position;
@@ -86,7 +87,7 @@
       return this;
     }
 
-    public ForwardMethodSourceCode build(Position callerPosition) {
+    public ForwardMethodSourceCode build(ProgramMethod context, Position callerPosition) {
       return new ForwardMethodSourceCode(
           receiver,
           method,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 6a611dc..3bf8883 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -67,7 +67,6 @@
 
   static SynthesizingContext fromSyntheticInputClass(
       DexProgramClass clazz, DexType synthesizingContextType, AppView<?> appView) {
-    assert synthesizingContextType != null;
     // A context that is itself synthetic must denote a synthesizing context from which to ensure
     // hygiene. This synthesizing context type is encoded on the synthetic for intermediate builds.
     FeatureSplit featureSplit =
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
index 126355f..c81ad1c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -38,6 +38,7 @@
   private Kind originKind;
   private DexType superType;
   private DexTypeList interfaces = DexTypeList.empty();
+  private DexString sourceFile = null;
   private List<DexEncodedField> staticFields = new ArrayList<>();
   private List<DexEncodedField> instanceFields = new ArrayList<>();
   private List<DexEncodedMethod> directMethods = new ArrayList<>();
@@ -81,6 +82,11 @@
     return self();
   }
 
+  public B setSourceFile(DexString sourceFile) {
+    this.sourceFile = sourceFile;
+    return self();
+  }
+
   public B setStaticFields(List<DexEncodedField> fields) {
     staticFields.clear();
     staticFields.addAll(fields);
@@ -117,7 +123,6 @@
     ClassAccessFlags accessFlags =
         ClassAccessFlags.fromSharedAccessFlags(
             flag | Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
-    DexString sourceFile = null;
     NestHostClassAttribute nestHost = null;
     List<NestMemberClassAttribute> nestMembers = Collections.emptyList();
     EnclosingMethodAttribute enclosingMembers = null;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index ac15138..cbdf447 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -478,7 +478,11 @@
       AppView<?> appView) {
     if (shouldAnnotateSynthetics(appView.options())) {
       SyntheticMarker.addMarkerToClass(
-          externalSyntheticClass, kind, context, appView.dexItemFactory());
+          externalSyntheticClass,
+          kind,
+          context,
+          appView.dexItemFactory(),
+          appView.options().forceAnnotateSynthetics);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
index d68558d..1d9b5b9 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Pair;
 
 public class SyntheticMarker {
@@ -20,21 +21,23 @@
       DexProgramClass clazz,
       SyntheticKind kind,
       SynthesizingContext context,
-      DexItemFactory factory) {
+      DexItemFactory factory,
+      boolean dontRecordSynthesizingContext) {
     clazz.setAnnotations(
         clazz
             .annotations()
             .getWithAddedOrReplaced(
                 DexAnnotation.createAnnotationSynthesizedClass(
-                    kind, context.getSynthesizingContextType(), factory)));
+                    kind,
+                    dontRecordSynthesizingContext ? null : context.getSynthesizingContextType(),
+                    factory)));
   }
 
   public static SyntheticMarker stripMarkerFromClass(DexProgramClass clazz, AppView<?> appView) {
     SyntheticMarker marker = internalStripMarkerFromClass(clazz, appView);
     assert marker != NO_MARKER
-        || DexAnnotation.getSynthesizedClassAnnotationContextType(
-                clazz.annotations(), appView.dexItemFactory())
-            == null;
+        || !DexAnnotation.hasSynthesizedClassAnnotation(
+            clazz.annotations(), appView.dexItemFactory());
     return marker;
   }
 
@@ -67,6 +70,18 @@
       }
     }
     clazz.setAnnotations(DexAnnotationSet.empty());
+    if (context == null) {
+      // If the class is marked as synthetic but has no synthesizing context, then we read the
+      // context type as the prefix. This happens for desugared library builds where the context of
+      // the generated
+      // synthetics becomes themselves. Using the original context could otherwise have referenced
+      // a type in the non-rewritten library and cause an non-rewritten output type.
+      String prefix = SyntheticNaming.getPrefixForExternalSyntheticType(kind, clazz.type);
+      context =
+          appView
+              .dexItemFactory()
+              .createType(DescriptorUtils.getDescriptorFromClassBinaryName(prefix));
+    }
     return new SyntheticMarker(
         kind, SynthesizingContext.fromSyntheticInputClass(clazz, context, appView));
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 487cb53..67bd52c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
+import java.util.function.Consumer;
 
 public class SyntheticMethodBuilder {
 
@@ -29,6 +30,10 @@
   private CfVersion classFileVersion;
   private SyntheticCodeGenerator codeGenerator = null;
   private MethodAccessFlags accessFlags = null;
+  private MethodTypeSignature genericSignature = MethodTypeSignature.noSignature();
+  private DexAnnotationSet annotations = DexAnnotationSet.empty();
+  private ParameterAnnotationsList parameterAnnotationsList = ParameterAnnotationsList.empty();
+  private Consumer<DexEncodedMethod> onBuildConsumer = null;
 
   SyntheticMethodBuilder(SyntheticClassBuilder<?, ?> parent) {
     this.factory = parent.getFactory();
@@ -71,6 +76,27 @@
     return this;
   }
 
+  public SyntheticMethodBuilder setGenericSignature(MethodTypeSignature genericSignature) {
+    this.genericSignature = genericSignature;
+    return this;
+  }
+
+  public SyntheticMethodBuilder setAnnotations(DexAnnotationSet annotations) {
+    this.annotations = annotations;
+    return this;
+  }
+
+  public SyntheticMethodBuilder setParameterAnnotationsList(
+      ParameterAnnotationsList parameterAnnotationsList) {
+    this.parameterAnnotationsList = parameterAnnotationsList;
+    return this;
+  }
+
+  public SyntheticMethodBuilder setOnBuildConsumer(Consumer<DexEncodedMethod> onBuildConsumer) {
+    this.onBuildConsumer = onBuildConsumer;
+    return this;
+  }
+
   DexEncodedMethod build() {
     assert name != null;
     boolean isCompilerSynthesized = true;
@@ -79,13 +105,19 @@
         new DexEncodedMethod(
             methodSignature,
             getAccessFlags(),
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
+            genericSignature,
+            annotations,
+            parameterAnnotationsList,
             getCodeObject(methodSignature),
             isCompilerSynthesized,
             classFileVersion);
-    assert isValidSyntheticMethod(method);
+    // Companion class method may have different properties.
+    assert isValidSyntheticMethod(method)
+        || SyntheticNaming.isSynthetic(
+            holderType.asClassReference(), null, SyntheticNaming.SyntheticKind.COMPANION_CLASS);
+    if (onBuildConsumer != null) {
+      onBuildConsumer.accept(method);
+    }
     return method;
   }
 
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index cb1e91b..8b19177 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -24,7 +24,8 @@
   public enum SyntheticKind {
     // Class synthetics.
     RECORD_TAG("", false, true, true),
-    COMPANION_CLASS("CompanionClass", false),
+    COMPANION_CLASS("-CC", false, true),
+    EMULATED_INTERFACE_CLASS("-EL", false, true),
     LAMBDA("Lambda", false),
     INIT_TYPE_ARGUMENT("-IA", false, true),
     HORIZONTAL_INIT_TYPE_ARGUMENT_1(SYNTHETIC_CLASS_SEPARATOR + "IA$1", false, true),
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 36ebfd8..a01f7dc 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -854,7 +854,7 @@
                 DexString.EMPTY_ARRAY);
         Code code =
             new SynthesizedCode(
-                callerPosition -> new ReturnVoidCode(voidReturnMethod, callerPosition));
+                (ignored, callerPosition) -> new ReturnVoidCode(voidReturnMethod, callerPosition));
         DexEncodedMethod method =
             new DexEncodedMethod(
                 voidReturnMethod,
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsRuleOnThrowingMethodTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsRuleOnThrowingMethodTest.java
new file mode 100644
index 0000000..ba02019
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsRuleOnThrowingMethodTest.java
@@ -0,0 +1,51 @@
+// 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.shaking.assumenosideeffects;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssumeNoSideEffectsRuleOnThrowingMethodTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public AssumeNoSideEffectsRuleOnThrowingMethodTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class)
+        .addKeepMainRule(Main.class)
+        .addKeepRules("-assumenosideeffects class " + Main.class.getTypeName() + " { ouch(); }")
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  static class Main {
+    public static void main(String[] args) {
+      ouch();
+      System.out.println("Hello world!");
+    }
+
+    static void ouch() {
+      throw new RuntimeException();
+    }
+  }
+}