Add CF attribute to compiler synthetics in intermediate mode.

Bug: 147485959
Bug: 158159959
Change-Id: If6efe30512a033fb33e09854fc76004949c8f0b8
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 26bbc5e..e2b7f93 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -819,7 +819,8 @@
               directMethods,
               virtualMethods,
               dexItemFactory.getSkipNameValidationForTesting(),
-              checksumSupplier);
+              checksumSupplier,
+              null);
       classCollection.accept(clazz);  // Update the application object.
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index baf5365..3e29db5 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -8,13 +8,56 @@
 import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.synthesis.SyntheticMarker;
 import java.util.List;
 import java.util.function.Predicate;
 
 /** Kind of the application class. Can be program, classpath or library. */
 public class ClassKind<C extends DexClass> {
   public static ClassKind<DexProgramClass> PROGRAM =
-      new ClassKind<>(DexProgramClass::new, DexClass::isProgramClass);
+      new ClassKind<>(
+          (type,
+              originKind,
+              origin,
+              accessFlags,
+              superType,
+              interfaces,
+              sourceFile,
+              nestHost,
+              nestMembers,
+              enclosingMember,
+              innerClasses,
+              classSignature,
+              classAnnotations,
+              staticFields,
+              instanceFields,
+              directMethods,
+              virtualMethods,
+              skipNameValidationForTesting,
+              checksumSupplier,
+              syntheticMarker) ->
+              new DexProgramClass(
+                  type,
+                  originKind,
+                  origin,
+                  accessFlags,
+                  superType,
+                  interfaces,
+                  sourceFile,
+                  nestHost,
+                  nestMembers,
+                  enclosingMember,
+                  innerClasses,
+                  classSignature,
+                  classAnnotations,
+                  staticFields,
+                  instanceFields,
+                  directMethods,
+                  virtualMethods,
+                  skipNameValidationForTesting,
+                  checksumSupplier,
+                  syntheticMarker),
+          DexClass::isProgramClass);
   public static ClassKind<DexClasspathClass> CLASSPATH =
       new ClassKind<>(
           (type,
@@ -35,7 +78,8 @@
               directMethods,
               virtualMethods,
               skipNameValidationForTesting,
-              checksumSupplier) ->
+              checksumSupplier,
+              syntheticMarker) ->
               new DexClasspathClass(
                   type,
                   kind,
@@ -76,7 +120,8 @@
               directMethods,
               virtualMethods,
               skipNameValidationForTesting,
-              checksumSupplier) ->
+              checksumSupplier,
+              syntheticMarker) ->
               new DexLibraryClass(
                   type,
                   kind,
@@ -118,7 +163,8 @@
         DexEncodedMethod[] directMethods,
         DexEncodedMethod[] virtualMethods,
         boolean skipNameValidationForTesting,
-        ChecksumSupplier checksumSupplier);
+        ChecksumSupplier checksumSupplier,
+        SyntheticMarker syntheticMarker);
   }
 
   private final Factory<C> factory;
@@ -148,7 +194,8 @@
       DexEncodedMethod[] directMethods,
       DexEncodedMethod[] virtualMethods,
       boolean skipNameValidationForTesting,
-      ChecksumSupplier checksumSupplier) {
+      ChecksumSupplier checksumSupplier,
+      SyntheticMarker syntheticMarker) {
     return factory.create(
         type,
         kind,
@@ -168,7 +215,8 @@
         directMethods,
         virtualMethods,
         skipNameValidationForTesting,
-        checksumSupplier);
+        checksumSupplier,
+        syntheticMarker);
   }
 
   public boolean isOfKind(DexClass clazz) {
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 d1e6951..0576326 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -359,19 +359,12 @@
   }
 
   public static DexAnnotation createAnnotationSynthesizedClass(
-      SyntheticKind kind, DexType synthesizingContext, DexItemFactory dexItemFactory) {
+      SyntheticKind kind, DexItemFactory dexItemFactory) {
     DexAnnotationElement kindElement =
         new DexAnnotationElement(
             dexItemFactory.kindString,
             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};
-    }
+    DexAnnotationElement[] elements = new DexAnnotationElement[] {kindElement};
     return new DexAnnotation(
         VISIBILITY_BUILD,
         new DexEncodedAnnotation(dexItemFactory.annotationSynthesizedClass, elements));
@@ -379,10 +372,10 @@
 
   public static boolean hasSynthesizedClassAnnotation(
       DexAnnotationSet annotations, DexItemFactory factory) {
-    return getSynthesizedClassAnnotationContextType(annotations, factory) != null;
+    return getSynthesizedClassAnnotationInfo(annotations, factory) != null;
   }
 
-  public static Pair<SyntheticKind, DexType> getSynthesizedClassAnnotationContextType(
+  public static SyntheticKind getSynthesizedClassAnnotationInfo(
       DexAnnotationSet annotations, DexItemFactory factory) {
     if (annotations.size() != 1) {
       return null;
@@ -392,7 +385,7 @@
       return null;
     }
     int length = annotation.annotation.elements.length;
-    if (length != 1 && length != 2) {
+    if (length != 1) {
       return null;
     }
     assert factory.kindString.isLessThan(factory.valueString);
@@ -406,20 +399,7 @@
     SyntheticKind kind =
         SyntheticNaming.SyntheticKind.fromDescriptor(
             kindElement.value.asDexValueString().getValue().toString());
-    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;
-    }
-    if (!valueElement.value.isDexValueType()) {
-      return null;
-    }
-    return new Pair<>(kind, valueElement.value.asDexValueType().getValue());
+    return kind;
   }
 
   public DexAnnotation rewrite(Function<DexEncodedAnnotation, DexEncodedAnnotation> rewriter) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 1a2bc2b..315b8a1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.synthesis.SyntheticMarker;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.android.tools.r8.utils.structural.StructuralItem;
@@ -53,6 +54,8 @@
 
   private final ChecksumSupplier checksumSupplier;
 
+  private SyntheticMarker syntheticMarker;
+
   public DexProgramClass(
       DexType type,
       Kind originKind,
@@ -72,7 +75,8 @@
       DexEncodedMethod[] directMethods,
       DexEncodedMethod[] virtualMethods,
       boolean skipNameValidationForTesting,
-      ChecksumSupplier checksumSupplier) {
+      ChecksumSupplier checksumSupplier,
+      SyntheticMarker syntheticMarker) {
     super(
         sourceFile,
         interfaces,
@@ -95,6 +99,50 @@
     assert classAnnotations != null;
     this.originKind = originKind;
     this.checksumSupplier = checksumSupplier;
+    this.syntheticMarker = syntheticMarker;
+  }
+
+  public DexProgramClass(
+      DexType type,
+      Kind originKind,
+      Origin origin,
+      ClassAccessFlags accessFlags,
+      DexType superType,
+      DexTypeList interfaces,
+      DexString sourceFile,
+      NestHostClassAttribute nestHost,
+      List<NestMemberClassAttribute> nestMembers,
+      EnclosingMethodAttribute enclosingMember,
+      List<InnerClassAttribute> innerClasses,
+      ClassSignature classSignature,
+      DexAnnotationSet classAnnotations,
+      DexEncodedField[] staticFields,
+      DexEncodedField[] instanceFields,
+      DexEncodedMethod[] directMethods,
+      DexEncodedMethod[] virtualMethods,
+      boolean skipNameValidationForTesting,
+      ChecksumSupplier checksumSupplier) {
+    this(
+        type,
+        originKind,
+        origin,
+        accessFlags,
+        superType,
+        interfaces,
+        sourceFile,
+        nestHost,
+        nestMembers,
+        enclosingMember,
+        innerClasses,
+        classSignature,
+        classAnnotations,
+        staticFields,
+        instanceFields,
+        directMethods,
+        virtualMethods,
+        skipNameValidationForTesting,
+        checksumSupplier,
+        null);
   }
 
   @Override
@@ -120,6 +168,15 @@
     return DexProgramClass::specify;
   }
 
+  public SyntheticMarker stripSyntheticInputMarker() {
+    SyntheticMarker marker = syntheticMarker;
+    // The synthetic input marker is "read once". It is stored only for identifying the input as
+    // synthetic and amending it to the SyntheticItems collection. After identification this field
+    // should not be used.
+    syntheticMarker = null;
+    return marker;
+  }
+
   private static void specify(StructuralSpecification<DexProgramClass, ?> spec) {
     spec.withItem(c -> c.type)
         .withItem(c -> c.superType)
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 4def125..3c01abb 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -37,6 +37,7 @@
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.synthesis.SyntheticMarker;
 import com.android.tools.r8.utils.AsmUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
@@ -58,6 +59,7 @@
 import java.util.function.Consumer;
 import java.util.zip.CRC32;
 import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.FieldVisitor;
@@ -119,6 +121,7 @@
     }
     reader.accept(
         new CreateDexClassVisitor<>(origin, classKind, reader.b, application, classConsumer),
+        new Attribute[] {SyntheticMarker.getMarkerAttributePrototype()},
         parsingOptions);
 
     // Read marker.
@@ -219,6 +222,7 @@
     private final List<DexEncodedMethod> virtualMethods = new ArrayList<>();
     private final Set<Wrapper<DexMethod>> methodSignatures = new HashSet<>();
     private boolean hasReachabilitySensitiveMethod = false;
+    private SyntheticMarker syntheticMarker = null;
 
     public CreateDexClassVisitor(
         Origin origin,
@@ -235,6 +239,15 @@
     }
 
     @Override
+    public void visitAttribute(Attribute attribute) {
+      SyntheticMarker marker = SyntheticMarker.readMarkerAttribute(attribute);
+      if (marker != null) {
+        assert syntheticMarker == null;
+        syntheticMarker = marker;
+      }
+    }
+
+    @Override
     public void visitInnerClass(String name, String outerName, String innerName, int access) {
       if (outerName != null && innerName != null) {
         String separator = DescriptorUtils.computeInnerClassSeparator(outerName, name, innerName);
@@ -452,7 +465,8 @@
               directMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
               virtualMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
               application.getFactory().getSkipNameValidationForTesting(),
-              getChecksumSupplier(classKind));
+              getChecksumSupplier(classKind),
+              syntheticMarker);
       InnerClassAttribute innerClassAttribute = clazz.getInnerClassAttributeForThisClass();
       // A member class should not be a local or anonymous class.
       if (innerClassAttribute != null && innerClassAttribute.getOuter() != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index f5845d4..e0c6d41 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -244,7 +244,8 @@
         },
         virtualMethods,
         factory.getSkipNameValidationForTesting(),
-        DexProgramClass::checksumFromType);
+        DexProgramClass::checksumFromType,
+        null);
   }
 
   private DexEncodedMethod[] synthesizeVirtualMethodsForVivifiedTypeWrapper(
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 5f23e34..0206e81 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -199,6 +199,7 @@
     }
     assert SyntheticNaming.verifyNotInternalSynthetic(name);
     writer.visit(version.raw(), access, name, signature, superName, interfaces);
+    appView.getSyntheticItems().writeAttributeIfIntermediateSyntheticClass(writer, clazz, appView);
     writeAnnotations(writer::visitAnnotation, clazz.annotations().annotations);
     ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations());
 
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 c81ad1c..ef3b4d2 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -160,6 +160,7 @@
             directMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
             virtualMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
             factory.getSkipNameValidationForTesting(),
-            c -> checksum);
+            c -> checksum,
+            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 cbdf447..de86111 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -362,8 +362,7 @@
           SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
           SynthesizingContext context = representative.getContext();
           context.registerPrefixRewriting(syntheticType, appView);
-          addSyntheticMarker(
-              representative.getKind(), representative.getHolder(), context, appView);
+          addSyntheticMarker(representative.getKind(), representative.getHolder(), appView);
           if (syntheticGroup.isDerivedFromMainDexList(mainDexInfo)) {
             derivedMainDexSynthetics.add(syntheticType);
           }
@@ -379,8 +378,7 @@
           SyntheticProgramClassDefinition representative = syntheticGroup.getRepresentative();
           SynthesizingContext context = representative.getContext();
           context.registerPrefixRewriting(syntheticType, appView);
-          addSyntheticMarker(
-              representative.getKind(), representative.getHolder(), context, appView);
+          addSyntheticMarker(representative.getKind(), representative.getHolder(), appView);
           if (syntheticGroup.isDerivedFromMainDexList(mainDexInfo)) {
             derivedMainDexSynthetics.add(syntheticType);
           }
@@ -474,24 +472,16 @@
   private static void addSyntheticMarker(
       SyntheticKind kind,
       DexProgramClass externalSyntheticClass,
-      SynthesizingContext context,
       AppView<?> appView) {
     if (shouldAnnotateSynthetics(appView.options())) {
-      SyntheticMarker.addMarkerToClass(
-          externalSyntheticClass,
-          kind,
-          context,
-          appView.dexItemFactory(),
-          appView.options().forceAnnotateSynthetics);
+      SyntheticMarker.addMarkerToClass(externalSyntheticClass, kind, appView.options());
     }
   }
 
   private static boolean shouldAnnotateSynthetics(InternalOptions options) {
     // Only intermediate builds have annotated synthetics to allow later sharing.
-    // This is currently also disabled on non-L8 CF to CF desugaring to avoid missing class
-    // references to the annotated classes.
-    // TODO(b/147485959): Find an alternative encoding for synthetics to avoid missing-class refs.
-    return options.intermediate && (!options.cfToCfDesugar || options.forceAnnotateSynthetics);
+    // Also, CF builds are marked in the writer using an attribute.
+    return options.intermediate && options.isGeneratingDex();
   }
 
   private <T extends SyntheticDefinition<?, T, ?>>
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 f709533..4fe7c2c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -30,6 +30,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -38,6 +39,7 @@
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
+import org.objectweb.asm.ClassWriter;
 
 public class SyntheticItems implements SyntheticDefinitionsProvider {
 
@@ -119,12 +121,8 @@
     assert synthetics.nextSyntheticId == 0;
     assert synthetics.committed.isEmpty();
     assert synthetics.pending.isEmpty();
-    if (appView.options().intermediate) {
-      // If the compilation is in intermediate mode the synthetics should just be passed through.
-      return;
-    }
     CommittedSyntheticsCollection.Builder builder = synthetics.committed.builder();
-    // TODO(b/158159959): Consider identifying synthetics in the input reader to speed this up.
+    // TODO(b/158159959): Consider populating the input synthetics when identified.
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       SyntheticMarker marker = SyntheticMarker.stripMarkerFromClass(clazz, appView);
       if (marker.isSyntheticMethods()) {
@@ -661,6 +659,23 @@
     return true;
   }
 
+  public void writeAttributeIfIntermediateSyntheticClass(
+      ClassWriter writer, DexProgramClass clazz, AppView<?> appView) {
+    if (!appView.options().intermediate || !appView.options().isGeneratingClassFiles()) {
+      return;
+    }
+    Iterator<SyntheticReference<?, ?, ?>> it =
+        committed.getNonLegacyItems(clazz.getType()).iterator();
+    if (it.hasNext()) {
+      SyntheticKind kind = it.next().getKind();
+      // When compiling intermediates there should not be any mergings as they may invalidate the
+      // single kind of a synthetic which is required for marking synthetics. This check could be
+      // relaxed to ensure that all kinds are equivalent if merging is possible.
+      assert !it.hasNext();
+      SyntheticMarker.writeMarkerAttribute(writer, kind);
+    }
+  }
+
   // Finalization of synthetic items.
 
   Result computeFinalSynthetics(AppView<?> appView) {
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 1d9b5b9..187b7dd 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
@@ -13,27 +13,90 @@
 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;
+import com.android.tools.r8.utils.InternalOptions;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ByteVector;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
 
 public class SyntheticMarker {
 
+  private static final String SYNTHETIC_MARKER_ATTRIBUTE_TYPE_NAME = "R8SynthesizedClass";
+
+  public static Attribute getMarkerAttributePrototype() {
+    return MarkerAttribute.PROTOTYPE;
+  }
+
+  public static void writeMarkerAttribute(ClassWriter writer, SyntheticKind kind) {
+    writer.visitAttribute(new MarkerAttribute(kind));
+  }
+
+  public static SyntheticMarker readMarkerAttribute(Attribute attribute) {
+    if (attribute instanceof MarkerAttribute) {
+      MarkerAttribute marker = (MarkerAttribute) attribute;
+      return new SyntheticMarker(marker.kind, null);
+    }
+    return null;
+  }
+
+  private static class MarkerAttribute extends Attribute {
+
+    private static final MarkerAttribute PROTOTYPE = new MarkerAttribute(null);
+
+    private SyntheticKind kind;
+
+    public MarkerAttribute(SyntheticKind kind) {
+      super(SYNTHETIC_MARKER_ATTRIBUTE_TYPE_NAME);
+      this.kind = kind;
+    }
+
+    @Override
+    protected Attribute read(
+        ClassReader classReader,
+        int offset,
+        int length,
+        char[] charBuffer,
+        int codeAttributeOffset,
+        Label[] labels) {
+      String kindDescriptor = classReader.readUTF8(offset, charBuffer);
+      SyntheticKind kind = SyntheticKind.fromDescriptor(kindDescriptor);
+      return new MarkerAttribute(kind);
+    }
+
+    @Override
+    protected ByteVector write(
+        ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) {
+      ByteVector byteVector = new ByteVector();
+      byteVector.putShort(classWriter.newUTF8(kind.descriptor));
+      return byteVector;
+    }
+  }
+
   public static void addMarkerToClass(
-      DexProgramClass clazz,
-      SyntheticKind kind,
-      SynthesizingContext context,
-      DexItemFactory factory,
-      boolean dontRecordSynthesizingContext) {
+      DexProgramClass clazz, SyntheticKind kind, InternalOptions options) {
+    // TODO(b/158159959): Consider moving this to the dex writer similar to the CF case.
+    assert !options.isGeneratingClassFiles();
     clazz.setAnnotations(
         clazz
             .annotations()
             .getWithAddedOrReplaced(
-                DexAnnotation.createAnnotationSynthesizedClass(
-                    kind,
-                    dontRecordSynthesizingContext ? null : context.getSynthesizingContextType(),
-                    factory)));
+                DexAnnotation.createAnnotationSynthesizedClass(kind, options.itemFactory)));
   }
 
   public static SyntheticMarker stripMarkerFromClass(DexProgramClass clazz, AppView<?> appView) {
+    if (clazz.originatesFromClassResource()) {
+      SyntheticMarker marker = clazz.stripSyntheticInputMarker();
+      if (marker == null) {
+        return NO_MARKER;
+      }
+      assert marker.getContext() == null;
+      DexType contextType =
+          getSyntheticContextType(clazz.type, marker.kind, appView.dexItemFactory());
+      SynthesizingContext context =
+          SynthesizingContext.fromSyntheticInputClass(clazz, contextType, appView);
+      return new SyntheticMarker(marker.kind, context);
+    }
     SyntheticMarker marker = internalStripMarkerFromClass(clazz, appView);
     assert marker != NO_MARKER
         || !DexAnnotation.hasSynthesizedClassAnnotation(
@@ -50,15 +113,13 @@
     if (!flags.isSynthetic() || flags.isAbstract() || flags.isEnum()) {
       return NO_MARKER;
     }
-    Pair<SyntheticKind, DexType> info =
-        DexAnnotation.getSynthesizedClassAnnotationContextType(
+    SyntheticKind kind =
+        DexAnnotation.getSynthesizedClassAnnotationInfo(
             clazz.annotations(), appView.dexItemFactory());
-    if (info == null) {
+    if (kind == null) {
       return NO_MARKER;
     }
     assert clazz.annotations().size() == 1;
-    SyntheticKind kind = info.getFirst();
-    DexType context = info.getSecond();
     if (kind.isSingleSyntheticMethod) {
       if (!clazz.interfaces.isEmpty()) {
         return NO_MARKER;
@@ -70,22 +131,17 @@
       }
     }
     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));
-    }
+    DexType context = getSyntheticContextType(clazz.type, kind, appView.dexItemFactory());
     return new SyntheticMarker(
         kind, SynthesizingContext.fromSyntheticInputClass(clazz, context, appView));
   }
 
+  private static DexType getSyntheticContextType(
+      DexType type, SyntheticKind kind, DexItemFactory factory) {
+    String prefix = SyntheticNaming.getPrefixForExternalSyntheticType(kind, type);
+    return factory.createType(DescriptorUtils.getDescriptorFromClassBinaryName(prefix));
+  }
+
   private static final SyntheticMarker NO_MARKER = new SyntheticMarker(null, null);
 
   private final SyntheticKind kind;
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index acfc021..00d6d45 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -91,9 +91,6 @@
 
   @Test
   public void testD8Merging() throws Exception {
-    assumeTrue(
-        "b/147485959: Merging does not happen for CF due to lack of synthetic annotations",
-        parameters.isDexRuntime());
     boolean intermediate = true;
     runD8Merging(intermediate);
   }