Reland "Parsing and emitting of modeled generic signature"

Bug: 129925954
Change-Id: I1e990244433095157cff1c31e649ef9d11111d01
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 17b7485..c295175 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.LazyLoadedDexApplication;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -173,6 +174,7 @@
             Collections.emptyList(),
             null,
             Collections.emptyList(),
+            ClassSignature.NO_CLASS_SIGNATURE,
             DexAnnotationSet.empty(),
             DexEncodedField.EMPTY_ARRAY,
             DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 075e0ce..9bee3f3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -892,12 +892,14 @@
           || options.getProguardConfiguration().hasApplyMappingFile()) {
         assert appView.rootSet().verifyKeptItemsAreKept(appView);
       }
-      assert appView
-          .graphLens()
-          .verifyMappingToOriginalProgram(
-              appView,
-              new ApplicationReader(inputApp.withoutMainDexList(), options, timing)
-                  .read(executorService));
+
+      assert options.testing.disableMappingToOriginalProgramVerification
+          || appView
+              .graphLens()
+              .verifyMappingToOriginalProgram(
+                  appView,
+                  new ApplicationReader(inputApp.withoutMainDexList(), options, timing)
+                      .read(executorService));
 
       // Report synthetic rules (only for testing).
       // TODO(b/120959039): Move this to being reported through the graph consumer.
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index b0f836c..6073b28 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -48,6 +48,7 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PredicateUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -66,6 +67,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 public class ApplicationWriter {
@@ -76,6 +78,7 @@
   public final NamingLens namingLens;
   public final InternalOptions options;
   private final CodeToKeep desugaredLibraryCodeToKeep;
+  private final Predicate<DexType> isTypeMissing;
   public List<Marker> markers;
   public List<DexString> markerStrings;
 
@@ -179,6 +182,8 @@
     this.namingLens = namingLens;
     this.proguardMapSupplier = proguardMapSupplier;
     this.programConsumer = consumer;
+    this.isTypeMissing =
+        PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
   }
 
   private List<VirtualFile> distribute(ExecutorService executorService)
@@ -454,12 +459,14 @@
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       EnclosingMethodAttribute enclosingMethod = clazz.getEnclosingMethodAttribute();
       List<InnerClassAttribute> innerClasses = clazz.getInnerClasses();
-      if (enclosingMethod == null && innerClasses.isEmpty()) {
+      if (enclosingMethod == null
+          && innerClasses.isEmpty()
+          && clazz.getClassSignature().hasNoSignature()) {
         continue;
       }
 
       // EnclosingMember translates directly to an enclosing class/method if present.
-      List<DexAnnotation> annotations = new ArrayList<>(1 + innerClasses.size());
+      List<DexAnnotation> annotations = new ArrayList<>(2 + innerClasses.size());
       if (enclosingMethod != null) {
         if (enclosingMethod.getEnclosingMethod() != null) {
           annotations.add(
@@ -507,6 +514,13 @@
         }
       }
 
+      if (clazz.getClassSignature().hasSignature()) {
+        annotations.add(
+            DexAnnotation.createSignatureAnnotation(
+                clazz.getClassSignature().toRenamedString(namingLens, isTypeMissing),
+                options.itemFactory));
+      }
+
       if (!annotations.isEmpty()) {
         // Append the annotations to annotations array of the class.
         DexAnnotation[] copy =
@@ -520,6 +534,7 @@
       // Clear the attribute structures now that they are represented in annotations.
       clazz.clearEnclosingMethodAttribute();
       clazz.clearInnerClasses();
+      clazz.clearClassSignature();
     }
   }
 
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 978a7e7..711eb47 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -51,6 +51,8 @@
 import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -60,6 +62,7 @@
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.Reporter;
 import com.google.common.io.ByteStreams;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -757,7 +760,8 @@
       }
 
       AttributesAndAnnotations attrs =
-          new AttributesAndAnnotations(type, annotationsDirectory.clazz, options.itemFactory);
+          new AttributesAndAnnotations(
+              type, origin, annotationsDirectory.clazz, options.itemFactory, options.reporter);
 
       Long finalChecksum = checksum;
       ChecksumSupplier checksumSupplier =
@@ -776,6 +780,7 @@
               Collections.emptyList(),
               attrs.getEnclosingMethodAttribute(),
               attrs.getInnerClasses(),
+              attrs.classSignature,
               attrs.getAnnotations(),
               staticFields,
               instanceFields,
@@ -1327,6 +1332,7 @@
     private EnclosingMethodAttribute enclosingMethodAttribute = null;
     private List<InnerClassAttribute> innerClasses = null;
     private List<DexAnnotation> lazyAnnotations = null;
+    private ClassSignature classSignature = ClassSignature.NO_CLASS_SIGNATURE;
 
     public DexAnnotationSet getAnnotations() {
       if (lazyAnnotations != null) {
@@ -1346,8 +1352,16 @@
       return enclosingMethodAttribute;
     }
 
+    public ClassSignature getClassSignature() {
+      return classSignature;
+    }
+
     public AttributesAndAnnotations(
-        DexType type, DexAnnotationSet annotations, DexItemFactory factory) {
+        DexType type,
+        Origin origin,
+        DexAnnotationSet annotations,
+        DexItemFactory factory,
+        Reporter reporter) {
       this.originalAnnotations = annotations;
       DexType enclosingClass = null;
       DexMethod enclosingMethod = null;
@@ -1378,6 +1392,12 @@
           } else {
             memberClasses.addAll(members);
           }
+        } else if (DexAnnotation.isSignatureAnnotation(annotation, factory)) {
+          ensureAnnotations(i);
+          String signature = DexAnnotation.getSignature(annotation);
+          classSignature =
+              GenericSignature.parseClassSignature(
+                  type.getName(), signature, origin, factory, reporter);
         } else {
           copyAnnotation(annotation);
         }
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 14fa13e..3929cfb 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.origin.Origin;
 import java.util.List;
 import java.util.function.Consumer;
@@ -26,6 +27,7 @@
           nestMembers,
           enclosingMember,
           innerClasses,
+          classSignature,
           annotations,
           staticFields,
           instanceFields,
@@ -45,6 +47,7 @@
             nestMembers,
             enclosingMember,
             innerClasses,
+            classSignature,
             annotations,
             staticFields,
             instanceFields,
@@ -65,6 +68,7 @@
           nestMembers,
           enclosingMember,
           innerClasses,
+          classSignature,
           annotations,
           staticFields,
           instanceFields,
@@ -84,6 +88,7 @@
             nestMembers,
             enclosingMember,
             innerClasses,
+            classSignature,
             annotations,
             staticFields,
             instanceFields,
@@ -106,6 +111,7 @@
         List<NestMemberClassAttribute> nestMembers,
         EnclosingMethodAttribute enclosingMember,
         List<InnerClassAttribute> innerClasses,
+        ClassSignature classSignature,
         DexAnnotationSet annotations,
         DexEncodedField[] staticFields,
         DexEncodedField[] instanceFields,
@@ -135,6 +141,7 @@
       List<NestMemberClassAttribute> nestMembers,
       EnclosingMethodAttribute enclosingMember,
       List<InnerClassAttribute> innerClasses,
+      ClassSignature classSignature,
       DexAnnotationSet annotations,
       DexEncodedField[] staticFields,
       DexEncodedField[] instanceFields,
@@ -154,6 +161,7 @@
         nestMembers,
         enclosingMember,
         innerClasses,
+        classSignature,
         annotations,
         staticFields,
         instanceFields,
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 7d18ce8..d883e3e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.InternalOptions;
@@ -61,6 +62,9 @@
   private NestHostClassAttribute nestHost;
   private final List<NestMemberClassAttribute> nestMembers;
 
+  /** Generic signature information if the attribute is present in the input */
+  protected ClassSignature classSignature;
+
   public DexClass(
       DexString sourceFile,
       DexTypeList interfaces,
@@ -75,6 +79,7 @@
       List<NestMemberClassAttribute> nestMembers,
       EnclosingMethodAttribute enclosingMethod,
       List<InnerClassAttribute> innerClasses,
+      ClassSignature classSignature,
       DexAnnotationSet annotations,
       Origin origin,
       boolean skipNameValidationForTesting) {
@@ -94,6 +99,9 @@
     assert nestMembers != null;
     this.enclosingMethod = enclosingMethod;
     this.innerClasses = innerClasses;
+    assert classSignature != null;
+    this.classSignature = classSignature;
+    assert GenericSignatureUtils.verifyNoDuplicateGenericDefinitions(classSignature, annotations);
     if (type == superType) {
       throw new CompilationError("Class " + type.toString() + " cannot extend itself");
     }
@@ -760,6 +768,10 @@
     innerClasses.clear();
   }
 
+  public void clearClassSignature() {
+    classSignature = ClassSignature.NO_CLASS_SIGNATURE;
+  }
+
   public void removeInnerClasses(Predicate<InnerClassAttribute> predicate) {
     innerClasses.removeIf(predicate::test);
   }
@@ -785,6 +797,14 @@
     throw new Unreachable();
   }
 
+  public ClassSignature getClassSignature() {
+    return classSignature;
+  }
+
+  public void setClassSignature(ClassSignature classSignature) {
+    this.classSignature = classSignature;
+  }
+
   public boolean isLocalClass() {
     InnerClassAttribute innerClass = getInnerClassAttributeForThisClass();
     // The corresponding enclosing-method attribute might be not available, e.g., CF version 50.
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 118a04d..410f985 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
 import com.android.tools.r8.origin.Origin;
 import java.util.List;
@@ -26,6 +27,7 @@
       List<NestMemberClassAttribute> nestMembers,
       EnclosingMethodAttribute enclosingMember,
       List<InnerClassAttribute> innerClasses,
+      ClassSignature classSignature,
       DexAnnotationSet annotations,
       DexEncodedField[] staticFields,
       DexEncodedField[] instanceFields,
@@ -46,6 +48,7 @@
         nestMembers,
         enclosingMember,
         innerClasses,
+        classSignature,
         annotations,
         origin,
         skipNameValidationForTesting);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 00dfa0b..cdf97ab 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -62,6 +62,7 @@
 public class DexItemFactory {
 
   public static final String throwableDescriptorString = "Ljava/lang/Throwable;";
+  public static final String dalvikAnnotationSignatureString = "Ldalvik/annotation/Signature;";
 
   /** Set of types that may be synthesized during compilation. */
   private final Set<DexType> possibleCompilerSynthesizedTypes = Sets.newIdentityHashSet();
@@ -541,7 +542,7 @@
   public final DexType annotationMethodParameters =
       createStaticallyKnownType("Ldalvik/annotation/MethodParameters;");
   public final DexType annotationSignature =
-      createStaticallyKnownType("Ldalvik/annotation/Signature;");
+      createStaticallyKnownType(dalvikAnnotationSignatureString);
   public final DexType annotationSourceDebugExtension =
       createStaticallyKnownType("Ldalvik/annotation/SourceDebugExtension;");
   public final DexType annotationThrows = createStaticallyKnownType("Ldalvik/annotation/Throws;");
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 22920eb..dcb4ed3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
 import com.android.tools.r8.origin.Origin;
 import java.util.Arrays;
@@ -27,6 +28,7 @@
       List<NestMemberClassAttribute> nestMembers,
       EnclosingMethodAttribute enclosingMember,
       List<InnerClassAttribute> innerClasses,
+      ClassSignature classSignature,
       DexAnnotationSet annotations,
       DexEncodedField[] staticFields,
       DexEncodedField[] instanceFields,
@@ -47,6 +49,7 @@
         nestMembers,
         enclosingMember,
         innerClasses,
+        classSignature,
         annotations,
         origin,
         skipNameValidationForTesting);
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 51959bc..e61ff61 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_ARGUMENTS;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
 import static com.google.common.base.Predicates.alwaysTrue;
 
@@ -11,12 +12,15 @@
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
 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.utils.TraversalContinuation;
+import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -62,6 +66,7 @@
       List<NestMemberClassAttribute> nestMembers,
       EnclosingMethodAttribute enclosingMember,
       List<InnerClassAttribute> innerClasses,
+      ClassSignature classSignature,
       DexAnnotationSet classAnnotations,
       DexEncodedField[] staticFields,
       DexEncodedField[] instanceFields,
@@ -81,6 +86,7 @@
         nestMembers,
         enclosingMember,
         innerClasses,
+        classSignature,
         classAnnotations,
         staticFields,
         instanceFields,
@@ -103,6 +109,7 @@
       List<NestMemberClassAttribute> nestMembers,
       EnclosingMethodAttribute enclosingMember,
       List<InnerClassAttribute> innerClasses,
+      ClassSignature classSignature,
       DexAnnotationSet classAnnotations,
       DexEncodedField[] staticFields,
       DexEncodedField[] instanceFields,
@@ -125,6 +132,7 @@
         nestMembers,
         enclosingMember,
         innerClasses,
+        classSignature,
         classAnnotations,
         origin,
         skipNameValidationForTesting);
@@ -283,6 +291,7 @@
       for (InnerClassAttribute attribute : getInnerClasses()) {
         attribute.collectIndexedItems(indexedItems);
       }
+      // We are explicitly not adding items referenced in signatures.
       forEachProgramField(field -> field.collectIndexedItems(indexedItems));
       forEachProgramMethod(method -> method.collectIndexedItems(indexedItems, graphLens, rewriter));
     }
@@ -296,6 +305,7 @@
   void collectMixedSectionItems(MixedSectionCollection mixedItems) {
     assert getEnclosingMethodAttribute() == null;
     assert getInnerClasses().isEmpty();
+    assert !classSignature.hasSignature();
     if (hasClassOrMemberAnnotations()) {
       mixedItems.setAnnotationsDirectoryForClass(this, new DexAnnotationDirectory(this));
     }
@@ -305,6 +315,7 @@
   public void addDependencies(MixedSectionCollection collector) {
     assert getEnclosingMethodAttribute() == null;
     assert getInnerClasses().isEmpty();
+    assert !classSignature.hasSignature();
     // We only have a class data item if there are methods or fields.
     if (hasMethodsOrFields()) {
       collector.add(this);
@@ -474,7 +485,7 @@
       return;
     }
     addExtraInterfacesToInterfacesArray(extraInterfaces);
-    addExtraInterfacesToSignatureAnnotationIfPresent(extraInterfaces, factory);
+    addExtraInterfacesToSignatureIfPresent(extraInterfaces);
   }
 
   private void addExtraInterfacesToInterfacesArray(List<DexType> extraInterfaces) {
@@ -486,31 +497,22 @@
     interfaces = new DexTypeList(newInterfaces);
   }
 
-  private void addExtraInterfacesToSignatureAnnotationIfPresent(
-      List<DexType> extraInterfaces, DexItemFactory factory) {
-    // We need to introduce in the dalvik.annotation.Signature annotation the extra interfaces.
+  private void addExtraInterfacesToSignatureIfPresent(List<DexType> extraInterfaces) {
+    // We need to introduce the extra interfaces to the generic signature.
     // At this point we cheat and pretend the extraInterfaces simply don't use any generic types.
-    DexAnnotation[] annotations = annotations().annotations;
-    for (int i = 0; i < annotations.length; i++) {
-      DexAnnotation annotation = annotations[i];
-      if (DexAnnotation.isSignatureAnnotation(annotation, factory)) {
-        DexAnnotation[] rewrittenAnnotations = annotations.clone();
-        rewrittenAnnotations[i] = rewriteSignatureAnnotation(annotation, extraInterfaces, factory);
-        setAnnotations(new DexAnnotationSet(rewrittenAnnotations));
-        // There is at most one signature annotation, so we can return here.
-        return;
-      }
+    if (classSignature.hasNoSignature() || extraInterfaces.isEmpty()) {
+      return;
     }
-  }
-
-  private DexAnnotation rewriteSignatureAnnotation(
-      DexAnnotation annotation, List<DexType> extraInterfaces, DexItemFactory factory) {
-    String signature = DexAnnotation.getSignature(annotation);
-    StringBuilder newSignatureBuilder = new StringBuilder(signature);
+    ImmutableList.Builder<ClassTypeSignature> interfacesBuilder =
+        ImmutableList.<ClassTypeSignature>builder().addAll(classSignature.superInterfaceSignatures);
     for (DexType extraInterface : extraInterfaces) {
-      newSignatureBuilder.append(extraInterface.descriptor.toString());
+      interfacesBuilder.add(new ClassTypeSignature(extraInterface, EMPTY_TYPE_ARGUMENTS));
     }
-    return DexAnnotation.createSignatureAnnotation(newSignatureBuilder.toString(), factory);
+    classSignature =
+        new ClassSignature(
+            classSignature.formalTypeParameters,
+            classSignature.superClassSignature,
+            interfacesBuilder.build());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 8ad3c8c..5cd3f8f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.utils.DescriptorUtils.getClassBinaryNameFromDescriptor;
 import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
+import static com.google.common.base.Predicates.alwaysTrue;
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.NamingLens;
@@ -15,6 +16,7 @@
 import java.lang.reflect.GenericSignatureFormatError;
 import java.nio.CharBuffer;
 import java.util.List;
+import java.util.function.Predicate;
 
 /**
  * Internal encoding of the generics signature attribute as defined by JVMS 7 $ 4.3.4.
@@ -219,18 +221,19 @@
       }
     }
 
-    public String toRenamedString(NamingLens namingLens) {
+    public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
       if (hasNoSignature()) {
         return null;
       }
-      GenericSignaturePrinter genericSignaturePrinter = new GenericSignaturePrinter(namingLens);
+      GenericSignaturePrinter genericSignaturePrinter =
+          new GenericSignaturePrinter(namingLens, isTypeMissing);
       genericSignaturePrinter.visitClassSignature(this);
       return genericSignaturePrinter.toString();
     }
 
     @Override
     public String toString() {
-      return toRenamedString(NamingLens.getIdentityLens());
+      return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
index b671a7b..1b23c85 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
@@ -13,13 +13,16 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.DescriptorUtils;
 import java.util.List;
+import java.util.function.Predicate;
 
 public class GenericSignaturePrinter implements GenericSignatureVisitor {
 
   private final NamingLens namingLens;
+  private final Predicate<DexType> isTypeMissing;
 
-  public GenericSignaturePrinter(NamingLens namingLens) {
+  public GenericSignaturePrinter(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
     this.namingLens = namingLens;
+    this.isTypeMissing = isTypeMissing;
   }
 
   private final StringBuilder sb = new StringBuilder();
@@ -117,9 +120,14 @@
         sb.append("L").append(DescriptorUtils.getBinaryNameFromDescriptor(renamedString));
       } else {
         assert classTypeSignature.enclosingTypeSignature != null;
-        String outerDescriptor =
-            namingLens.lookupDescriptor(classTypeSignature.enclosingTypeSignature.type).toString();
+        DexType enclosingType = classTypeSignature.enclosingTypeSignature.type;
+        String outerDescriptor = namingLens.lookupDescriptor(enclosingType).toString();
         String innerClassName = DescriptorUtils.getInnerClassName(outerDescriptor, renamedString);
+        if (innerClassName == null && isTypeMissing.test(classTypeSignature.type)) {
+          assert renamedString.equals(classTypeSignature.type.toDescriptorString());
+          innerClassName =
+              DescriptorUtils.getInnerClassName(enclosingType.toDescriptorString(), renamedString);
+        }
         if (innerClassName == null) {
           // We can no longer encode the inner name in the generic signature.
           return;
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
new file mode 100644
index 0000000..ddabc37
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -0,0 +1,241 @@
+// Copyright (c) 2020, 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.graph;
+
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_SUPER_INTERFACES;
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_ARGUMENTS;
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_PARAMS;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
+import static com.android.tools.r8.graph.GenericSignature.StarFieldTypeSignature.STAR_FIELD_TYPE_SIGNATURE;
+
+import com.android.tools.r8.graph.GenericSignature.ArrayTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.ArrayList;
+import java.util.List;
+
+public class GenericSignatureTypeRewriter {
+
+  private final AppView<?> appView;
+  private final DexProgramClass context;
+
+  private final FieldTypeSignature objectTypeSignature;
+
+  public GenericSignatureTypeRewriter(AppView<?> appView, DexProgramClass context) {
+    this.appView = appView;
+    this.context = context;
+    objectTypeSignature =
+        new ClassTypeSignature(appView.dexItemFactory().objectType, EMPTY_TYPE_ARGUMENTS);
+  }
+
+  public ClassSignature rewrite(ClassSignature classSignature) {
+    if (classSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+      return classSignature;
+    }
+    return new ClassSignatureRewriter().run(classSignature);
+  }
+
+  private class ClassSignatureRewriter implements GenericSignatureVisitor {
+
+    private final List<FormalTypeParameter> rewrittenTypeParameters = new ArrayList<>();
+    private ClassTypeSignature rewrittenSuperClass;
+    private final List<ClassTypeSignature> rewrittenSuperInterfaces = new ArrayList<>();
+
+    @Override
+    public void visitClassSignature(ClassSignature classSignature) {
+      classSignature.visit(this);
+    }
+
+    @Override
+    public void visitFormalTypeParameters(List<FormalTypeParameter> formalTypeParameters) {
+      for (FormalTypeParameter formalTypeParameter : formalTypeParameters) {
+        rewrittenTypeParameters.add(new FormalTypeParameterRewriter().run(formalTypeParameter));
+      }
+    }
+
+    @Override
+    public void visitSuperClass(ClassTypeSignature classTypeSignature) {
+      rewrittenSuperClass = new ClassTypeSignatureRewriter(true).run(classTypeSignature);
+      if (rewrittenSuperClass == null) {
+        rewrittenSuperClass =
+            new ClassTypeSignature(appView.dexItemFactory().objectType, EMPTY_TYPE_ARGUMENTS);
+      }
+    }
+
+    @Override
+    public void visitSuperInterface(ClassTypeSignature classTypeSignature) {
+      ClassTypeSignature superInterface =
+          new ClassTypeSignatureRewriter(true).run(classTypeSignature);
+      if (superInterface != null) {
+        rewrittenSuperInterfaces.add(superInterface);
+      }
+    }
+
+    private ClassSignature run(ClassSignature classSignature) {
+      classSignature.visit(this);
+      if (rewrittenTypeParameters.isEmpty()
+          && rewrittenSuperInterfaces.isEmpty()
+          && rewrittenSuperClass.isNoSignature()
+          && rewrittenSuperClass.type == appView.dexItemFactory().objectType) {
+        return ClassSignature.NO_CLASS_SIGNATURE;
+      }
+      return new ClassSignature(
+          rewrittenTypeParameters.isEmpty() ? EMPTY_TYPE_PARAMS : rewrittenTypeParameters,
+          rewrittenSuperClass,
+          rewrittenSuperInterfaces.isEmpty() ? EMPTY_SUPER_INTERFACES : rewrittenSuperInterfaces);
+    }
+  }
+
+  private class FormalTypeParameterRewriter implements GenericSignatureVisitor {
+
+    private FieldTypeSignature rewrittenClassBound = NO_FIELD_TYPE_SIGNATURE;
+    private final List<FieldTypeSignature> rewrittenInterfaceBounds = new ArrayList<>();
+
+    @Override
+    public void visitClassBound(FieldTypeSignature fieldSignature) {
+      rewrittenClassBound = new TypeSignatureRewriter().run(fieldSignature);
+    }
+
+    @Override
+    public void visitInterfaceBound(FieldTypeSignature fieldSignature) {
+      FieldTypeSignature interfaceBound = new TypeSignatureRewriter().run(fieldSignature);
+      if (interfaceBound != null) {
+        rewrittenInterfaceBounds.add(interfaceBound);
+      }
+    }
+
+    private FormalTypeParameter run(FormalTypeParameter formalTypeParameter) {
+      formalTypeParameter.visit(this);
+      // Guard against the case where we have <T::...> that is, no class or interfaces bounds.
+      if (rewrittenInterfaceBounds.isEmpty()
+          && (rewrittenClassBound == null || !rewrittenClassBound.hasSignature())) {
+        rewrittenClassBound = objectTypeSignature;
+      }
+      return new FormalTypeParameter(
+          formalTypeParameter.name,
+          rewrittenClassBound == null ? NO_FIELD_TYPE_SIGNATURE : rewrittenClassBound,
+          rewrittenInterfaceBounds.isEmpty() ? EMPTY_TYPE_ARGUMENTS : rewrittenInterfaceBounds);
+    }
+  }
+
+  private class TypeSignatureRewriter implements GenericSignatureVisitor {
+
+    private TypeSignature run(TypeSignature typeSignature) {
+      if (typeSignature.isBaseTypeSignature()) {
+        return typeSignature;
+      }
+      assert typeSignature.isFieldTypeSignature();
+      return run(typeSignature.asFieldTypeSignature());
+    }
+
+    private FieldTypeSignature run(FieldTypeSignature fieldTypeSignature) {
+      if (fieldTypeSignature.isStar()) {
+        return fieldTypeSignature;
+      }
+      if (fieldTypeSignature.isTypeVariableSignature()) {
+        return fieldTypeSignature;
+      }
+      if (fieldTypeSignature.isArrayTypeSignature()) {
+        ArrayTypeSignature arrayTypeSignature = fieldTypeSignature.asArrayTypeSignature();
+        TypeSignature rewrittenElement = run(arrayTypeSignature.elementSignature);
+        if (rewrittenElement == null) {
+          return new ArrayTypeSignature(objectTypeSignature);
+        }
+        return rewrittenElement.toArrayTypeSignature();
+      }
+      assert fieldTypeSignature.isClassTypeSignature();
+      ClassTypeSignature classTypeSignature = fieldTypeSignature.asClassTypeSignature();
+      if (classTypeSignature.isNoSignature()) {
+        return classTypeSignature;
+      }
+      return new ClassTypeSignatureRewriter(false).run(classTypeSignature);
+    }
+  }
+
+  private class ClassTypeSignatureRewriter implements GenericSignatureVisitor {
+
+    private final AppInfoWithLiveness appInfoWithLiveness;
+    private final boolean isSuperClassOrInterface;
+
+    // These fields are updated when iterating the modeled structure.
+    private DexType currentType;
+
+    // The following references are used to have a head and tail pointer to the classTypeSignature
+    // link we are building. The topClassSignature will have a reference to the top-most package
+    // and class-name. The parentClassSignature is a pointer pointing to the tail always and will
+    // be linked and updated when calling ClassTypeSignature.link.
+    private ClassTypeSignature topClassSignature;
+    private ClassTypeSignature parentClassSignature;
+
+    private ClassTypeSignatureRewriter(boolean isSuperClassOrInterface) {
+      appInfoWithLiveness =
+          appView.appInfo().hasLiveness() ? appView.appInfo().withLiveness() : null;
+      this.isSuperClassOrInterface = isSuperClassOrInterface;
+    }
+
+    @Override
+    public void visitSimpleClass(ClassTypeSignature classTypeSignature) {
+      currentType = getTarget(classTypeSignature.type);
+      if (currentType == null) {
+        return;
+      }
+      classTypeSignature.visit(this);
+    }
+
+    @Override
+    public void visitTypeArguments(List<FieldTypeSignature> typeArguments) {
+      ClassTypeSignature newClassTypeSignature;
+      if (typeArguments.isEmpty()) {
+        newClassTypeSignature = new ClassTypeSignature(currentType, EMPTY_TYPE_ARGUMENTS);
+      } else {
+        List<FieldTypeSignature> rewrittenTypeArguments = new ArrayList<>(typeArguments.size());
+        for (FieldTypeSignature typeArgument : typeArguments) {
+          if (typeArgument.isStar()) {
+            rewrittenTypeArguments.add(typeArgument);
+            continue;
+          }
+          FieldTypeSignature rewritten = new TypeSignatureRewriter().run(typeArgument);
+          if (rewritten != null) {
+            rewrittenTypeArguments.add(rewritten.asArgument(typeArgument.getWildcardIndicator()));
+          } else {
+            rewrittenTypeArguments.add(STAR_FIELD_TYPE_SIGNATURE);
+          }
+        }
+        newClassTypeSignature = new ClassTypeSignature(currentType, rewrittenTypeArguments);
+      }
+      if (topClassSignature == null) {
+        topClassSignature = newClassTypeSignature;
+        parentClassSignature = newClassTypeSignature;
+      } else {
+        ClassTypeSignature.link(parentClassSignature, newClassTypeSignature);
+        parentClassSignature = newClassTypeSignature;
+      }
+    }
+
+    private ClassTypeSignature run(ClassTypeSignature classTypeSignature) {
+      currentType = getTarget(classTypeSignature.type);
+      if (currentType == null) {
+        return null;
+      }
+      classTypeSignature.visit(this);
+      return topClassSignature;
+    }
+
+    private DexType getTarget(DexType type) {
+      if (appInfoWithLiveness != null && appInfoWithLiveness.wasPruned(type)) {
+        return null;
+      }
+      DexType rewrittenType = appView.graphLens().lookupType(type);
+      if (isSuperClassOrInterface && context.type == rewrittenType) {
+        return null;
+      }
+      return rewrittenType;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java
new file mode 100644
index 0000000..7c0dcd2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2020, 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.graph;
+
+import static com.android.tools.r8.graph.GenericSignature.ClassSignature.NO_CLASS_SIGNATURE;
+import static com.android.tools.r8.graph.GenericSignature.MethodTypeSignature.NO_METHOD_TYPE_SIGNATURE;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
+
+import com.android.tools.r8.graph.GenericSignature.DexDefinitionSignature;
+
+public class GenericSignatureUtils {
+
+  public static boolean verifyNoDuplicateGenericDefinitions(
+      DexDefinitionSignature<?> signature, DexAnnotationSet annotations) {
+    assert signature != null;
+    if (signature == NO_METHOD_TYPE_SIGNATURE
+        || signature == NO_FIELD_TYPE_SIGNATURE
+        || signature == NO_CLASS_SIGNATURE
+        || annotations == null) {
+      return true;
+    }
+    // The check is on the string descriptor to allow for not passing in a factory.
+    for (DexAnnotation annotation : annotations.annotations) {
+      assert !annotation
+          .getAnnotationType()
+          .descriptor
+          .toString()
+          .equals(DexItemFactory.dalvikAnnotationSignatureString);
+    }
+    return true;
+  }
+}
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 ea58f8d..4382eaa 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -32,6 +32,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueShort;
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -206,6 +207,7 @@
     private final List<NestMemberClassAttribute> nestMembers = new ArrayList<>();
     private EnclosingMethodAttribute enclosingMember = null;
     private final List<InnerClassAttribute> innerClasses = new ArrayList<>();
+    private ClassSignature classSignature = ClassSignature.NO_CLASS_SIGNATURE;
     private List<DexAnnotation> annotations = null;
     private List<DexAnnotationElement> defaultAnnotations = null;
     private final List<DexEncodedField> staticFields = new ArrayList<>();
@@ -329,9 +331,9 @@
       assert superName != null || name.equals(Constants.JAVA_LANG_OBJECT_NAME);
       superType = superName == null ? null : application.getTypeFromName(superName);
       this.interfaces = application.getTypeListFromNames(interfaces);
-      if (signature != null && !signature.isEmpty()) {
-        addAnnotation(DexAnnotation.createSignatureAnnotation(signature, application.getFactory()));
-      }
+      classSignature =
+          GenericSignature.parseClassSignature(
+              name, signature, origin, application.getFactory(), application.options.reporter);
     }
 
     @Override
@@ -410,6 +412,7 @@
               nestMembers,
               enclosingMember,
               innerClasses,
+              classSignature,
               createAnnotationSet(annotations, application.options),
               staticFields.toArray(DexEncodedField.EMPTY_ARRAY),
               instanceFields.toArray(DexEncodedField.EMPTY_ARRAY),
@@ -567,8 +570,8 @@
       this.desc = desc;
       this.value = value;
       if (signature != null && !signature.isEmpty()) {
-        addAnnotation(DexAnnotation.createSignatureAnnotation(
-            signature, parent.application.getFactory()));
+        addAnnotation(
+            DexAnnotation.createSignatureAnnotation(signature, parent.application.getFactory()));
       }
     }
 
@@ -648,7 +651,6 @@
     private void addAnnotation(DexAnnotation annotation) {
       getAnnotations().add(annotation);
     }
-
     private List<DexAnnotation> getAnnotations() {
       if (annotations == null) {
         annotations = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
index db92416..ec8d495 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.Collections;
@@ -75,6 +76,7 @@
               Collections.emptyList(),
               null,
               Collections.emptyList(),
+              ClassSignature.NO_CLASS_SIGNATURE,
               DexAnnotationSet.empty(),
               DexEncodedField.EMPTY_ARRAY,
               DexEncodedField.EMPTY_ARRAY,
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 5280f61..2d4fb70 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
@@ -22,6 +22,7 @@
 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.ClassSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ResolutionResult;
@@ -111,6 +112,7 @@
             Collections.emptyList(),
             null,
             Collections.emptyList(),
+            ClassSignature.NO_CLASS_SIGNATURE,
             DexAnnotationSet.empty(),
             DexEncodedField.EMPTY_ARRAY,
             DexEncodedField.EMPTY_ARRAY,
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 7936ffd..f2ea71d 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
@@ -25,6 +25,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.conversion.IRConverter;
@@ -230,6 +231,7 @@
         Collections.emptyList(),
         null,
         Collections.emptyList(),
+        ClassSignature.NO_CLASS_SIGNATURE,
         DexAnnotationSet.empty(),
         DexEncodedField.EMPTY_ARRAY, // No static fields.
         new DexEncodedField[] {wrapperField},
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index e58eeba..6d37c32 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
@@ -712,6 +713,7 @@
             Collections.emptyList(),
             null,
             Collections.emptyList(),
+            ClassSignature.NO_CLASS_SIGNATURE,
             DexAnnotationSet.empty(),
             DexEncodedField.EMPTY_ARRAY,
             DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 20c3ae4..67d389f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -26,6 +26,7 @@
 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.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
 import com.android.tools.r8.graph.MethodAccessFlags;
@@ -213,6 +214,7 @@
             Collections.emptyList(),
             null,
             Collections.emptyList(),
+            ClassSignature.NO_CLASS_SIGNATURE,
             DexAnnotationSet.empty(),
             DexEncodedField.EMPTY_ARRAY,
             DexEncodedField.EMPTY_ARRAY,
@@ -299,6 +301,7 @@
             Collections.emptyList(),
             null,
             Collections.emptyList(),
+            ClassSignature.NO_CLASS_SIGNATURE,
             DexAnnotationSet.empty(),
             DexEncodedField.EMPTY_ARRAY,
             DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index ecad620..cc6fec7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -160,6 +161,7 @@
             Collections.emptyList(),
             null,
             Collections.emptyList(),
+            ClassSignature.NO_CLASS_SIGNATURE,
             DexAnnotationSet.empty(),
             synthesizeStaticFields(),
             synthesizeInstanceFields(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 89dc96c..01b87d2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -23,6 +23,7 @@
 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.ClassSignature;
 import com.android.tools.r8.graph.NestMemberClassAttribute;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
@@ -185,6 +186,7 @@
         Collections.emptyList(),
         null,
         Collections.emptyList(),
+        ClassSignature.NO_CLASS_SIGNATURE,
         DexAnnotationSet.empty(),
         DexEncodedField.EMPTY_ARRAY,
         DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 613fac6..7b0d6b0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.ir.code.IRCode;
@@ -151,6 +152,7 @@
             Collections.emptyList(),
             null,
             Collections.emptyList(),
+            ClassSignature.NO_CLASS_SIGNATURE,
             DexAnnotationSet.empty(),
             DexEncodedField.EMPTY_ARRAY,
             DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 655ee2f..56d0a08 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -25,6 +25,7 @@
 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.ClassSignature;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -1391,7 +1392,7 @@
         Collections.emptyList(),
         null,
         Collections.emptyList(),
-        // TODO: Build dex annotations structure.
+        ClassSignature.NO_CLASS_SIGNATURE,
         DexAnnotationSet.empty(),
         DexEncodedField.EMPTY_ARRAY, // Static fields.
         DexEncodedField.EMPTY_ARRAY, // Instance fields.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index b1fcc81..7a81edc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodCollection;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -252,6 +253,7 @@
                       Collections.emptyList(),
                       null,
                       Collections.emptyList(),
+                      ClassSignature.NO_CLASS_SIGNATURE,
                       DexAnnotationSet.empty(),
                       DexEncodedField.EMPTY_ARRAY, // Static fields.
                       DexEncodedField.EMPTY_ARRAY, // Instance fields.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
index 16c9118..b199c47 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.ProgramPackage;
 import com.android.tools.r8.graph.ProgramPackageCollection;
 import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -121,6 +122,7 @@
               Collections.emptyList(),
               null,
               Collections.emptyList(),
+              ClassSignature.NO_CLASS_SIGNATURE,
               DexAnnotationSet.empty(),
               DexEncodedField.EMPTY_ARRAY,
               DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
index 5ddc8dd..92f0972 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -50,7 +51,8 @@
         Collections.emptyList(),
         buildEnclosingMethodAttribute(),
         buildInnerClasses(),
-        buildAnnotations(),
+        buildClassSignature(),
+        DexAnnotationSet.empty(),
         buildStaticFields(appView, feedback),
         buildInstanceFields(),
         buildDirectMethods(),
@@ -68,7 +70,7 @@
 
   protected abstract List<InnerClassAttribute> buildInnerClasses();
 
-  protected abstract DexAnnotationSet buildAnnotations();
+  protected abstract ClassSignature buildClassSignature();
 
   protected abstract DexEncodedMethod[] buildVirtualMethods();
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index 5b0b0b4..db7f56c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -23,6 +22,8 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue.DexValueNull;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -33,6 +34,7 @@
 import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
+import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.IntBox;
 import com.android.tools.r8.utils.InternalOptions;
@@ -84,14 +86,11 @@
   }
 
   @Override
-  protected DexAnnotationSet buildAnnotations() {
+  protected ClassSignature buildClassSignature() {
     // Kotlin-style lambdas supported by the merged may only contain optional signature and
     // kotlin metadata annotations. We remove the latter, but keep the signature if present.
-    String signature = id.signature;
-    return signature == null
-        ? DexAnnotationSet.empty()
-        : new DexAnnotationSet(
-            new DexAnnotation[]{DexAnnotation.createSignatureAnnotation(signature, factory)});
+    return GenericSignature.parseClassSignature(
+        origin, id.signature, new SynthesizedOrigin(origin, getClass()), factory, options.reporter);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 049748f..5a90f80 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -91,9 +91,6 @@
 
   public static boolean hasValidAnnotations(Kotlin kotlin, DexClass lambda) {
     for (DexAnnotation annotation : lambda.annotations().annotations) {
-      if (DexAnnotation.isSignatureAnnotation(annotation, kotlin.factory)) {
-        continue;
-      }
       if (annotation.annotation.type == kotlin.factory.kotlinMetadataType) {
         continue;
       }
@@ -104,13 +101,7 @@
 
   String validateAnnotations(AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
       throws LambdaStructureError {
-    String signature = null;
     for (DexAnnotation annotation : lambda.liveAnnotations(appView).annotations) {
-      if (DexAnnotation.isSignatureAnnotation(annotation, kotlin.factory)) {
-        signature = DexAnnotation.getSignature(annotation);
-        continue;
-      }
-
       if (annotation.annotation.type == appView.dexItemFactory().kotlinMetadataType) {
         // Ignore kotlin metadata on lambda classes. Metadata on synthetic
         // classes exists but is not used in the current Kotlin version (1.2.21)
@@ -123,7 +114,7 @@
           "unexpected annotation: " + annotation.annotation.type.toSourceString());
     }
     assert hasValidAnnotations(kotlin, lambda);
-    return signature;
+    return lambda.getClassSignature().toString();
   }
 
   void validateStaticFields(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
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 e28734b..06e3679 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -46,11 +46,13 @@
 import com.android.tools.r8.utils.AsmUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PredicateUtils;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Optional;
+import java.util.function.Predicate;
 import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassTooLargeException;
@@ -79,6 +81,7 @@
   private final NamingLens namingLens;
   private final InternalOptions options;
   private final Marker marker;
+  private final Predicate<DexType> isTypeMissing;
 
   public final ProguardMapSupplier proguardMapSupplier;
 
@@ -96,6 +99,8 @@
     assert marker != null;
     this.marker = marker;
     this.proguardMapSupplier = proguardMapSupplier;
+    this.isTypeMissing =
+        PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
   }
 
   public void write(ClassFileConsumer consumer) {
@@ -179,7 +184,7 @@
     }
     String desc = namingLens.lookupDescriptor(clazz.type).toString();
     String name = namingLens.lookupInternalName(clazz.type);
-    String signature = getSignature(clazz.annotations());
+    String signature = clazz.getClassSignature().toRenamedString(namingLens, isTypeMissing);
     String superName =
         clazz.type == options.itemFactory.objectType
             ? null
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index 92b4402..e1a49d8 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -13,6 +13,8 @@
 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.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -27,7 +29,7 @@
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
-// TODO(b/129925954): Reimplement this by using the internal encoding and transformation logic.
+// TODO(b/169516860): We should generalize this to handle rewriting of attributes in general.
 public class GenericSignatureRewriter {
 
   private final AppView<?> appView;
@@ -56,17 +58,27 @@
     ThreadUtils.processItems(
         classes,
         clazz -> {
+          GenericSignatureTypeRewriter genericSignatureTypeRewriter =
+              new GenericSignatureTypeRewriter(appView, clazz);
           GenericSignatureCollector genericSignatureCollector =
               new GenericSignatureCollector(clazz);
           GenericSignatureParser<DexType> genericSignatureParser =
               new GenericSignatureParser<>(genericSignatureCollector);
-          clazz.setAnnotations(
-              rewriteGenericSignatures(
-                  clazz.annotations(),
-                  genericSignatureParser::parseClassSignature,
-                  genericSignatureCollector::getRenamedSignature,
-                  (signature, e) ->
-                      options.warningInvalidSignature(clazz, clazz.getOrigin(), signature, e)));
+          ClassSignature classSignature = clazz.getClassSignature();
+          if (classSignature.hasSignature()) {
+            // TODO(b/129925954): We still have to rewrite to capture the lastWrittenType.
+            //  The design is utterly broken.
+            DexAnnotation classSignatureAnnotation =
+                DexAnnotation.createSignatureAnnotation(
+                    classSignature.toString(), options.itemFactory);
+            rewriteGenericSignatures(
+                new DexAnnotationSet(new DexAnnotation[] {classSignatureAnnotation}),
+                genericSignatureParser::parseClassSignature,
+                genericSignatureCollector::getRenamedSignature,
+                (signature, e) ->
+                    options.warningInvalidSignature(clazz, clazz.getOrigin(), signature, e));
+          }
+          clazz.setClassSignature(genericSignatureTypeRewriter.rewrite(classSignature));
           clazz.forEachField(
               field ->
                   field.setAnnotations(
@@ -91,6 +103,7 @@
         executorService);
   }
 
+  // TODO(b/129925954): Remove this when using modeled signatures for methods and fields.
   private DexAnnotationSet rewriteGenericSignatures(
       DexAnnotationSet annotations,
       Consumer<String> parser,
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
index f519f01..6bde745 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
@@ -73,6 +73,7 @@
             fixupNestMemberAttributes(clazz.getNestMembersClassAttributes()),
             fixupEnclosingMethodAttribute(clazz.getEnclosingMethodAttribute()),
             fixupInnerClassAttributes(clazz.getInnerClasses()),
+            clazz.getClassSignature(),
             clazz.annotations(),
             DexEncodedField.EMPTY_ARRAY,
             DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 5ae46e8..4fa3212 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -83,6 +83,9 @@
         assert !DexAnnotation.isMemberClassesAnnotation(annotation, dexItemFactory);
         assert !DexAnnotation.isEnclosingMethodAnnotation(annotation, dexItemFactory);
         assert !DexAnnotation.isEnclosingClassAnnotation(annotation, dexItemFactory);
+        // TODO(b/129925954): Signature is being represented as a class attribute.
+        assert !holder.isDexClass()
+            || !DexAnnotation.isSignatureAnnotation(annotation, dexItemFactory);
         if (config.exceptions && DexAnnotation.isThrowingAnnotation(annotation, dexItemFactory)) {
           return true;
         }
@@ -184,15 +187,6 @@
     }
   }
 
-  private static boolean hasSignatureAnnotation(DexProgramClass clazz, DexItemFactory itemFactory) {
-    for (DexAnnotation annotation : clazz.annotations().annotations) {
-      if (DexAnnotation.isSignatureAnnotation(annotation, itemFactory)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   public void run() {
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       stripAttributes(clazz);
@@ -291,6 +285,9 @@
           hasInnerClassesFromSet(clazz, classesToRetainInnerClassAttributeFor);
     }
     if (keptAnyway || keepForThisInnerClass || keepForThisEnclosingClass) {
+      if (!keep.signature) {
+        clazz.clearClassSignature();
+      }
       if (!keep.enclosingMethod) {
         clazz.clearEnclosingMethodAttribute();
       }
@@ -325,6 +322,7 @@
       // reflection. (Note that clearing these attributes can enable more vertical class merging.)
       clazz.clearEnclosingMethodAttribute();
       clazz.clearInnerClasses();
+      clazz.clearClassSignature();
     }
   }
 
@@ -368,7 +366,7 @@
         Map<DexType, DexProgramClass> enclosingClasses = new IdentityHashMap<>();
         Set<DexProgramClass> genericClasses = Sets.newIdentityHashSet();
         for (DexProgramClass clazz : appView.appInfo().classes()) {
-          if (hasSignatureAnnotation(clazz, appView.dexItemFactory())) {
+          if (clazz.getClassSignature().hasSignature()) {
             genericClasses.add(clazz);
           }
           for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
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 1091d09..80c47f2 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.NestHostClassAttribute;
 import com.android.tools.r8.graph.NestMemberClassAttribute;
@@ -71,7 +72,6 @@
     List<NestMemberClassAttribute> nestMembers = Collections.emptyList();
     EnclosingMethodAttribute enclosingMembers = null;
     List<InnerClassAttribute> innerClasses = Collections.emptyList();
-    DexAnnotationSet classAnnotations = DexAnnotationSet.empty();
     DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
     DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
     DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
@@ -106,7 +106,8 @@
         nestMembers,
         enclosingMembers,
         innerClasses,
-        classAnnotations,
+        ClassSignature.NO_CLASS_SIGNATURE,
+        DexAnnotationSet.empty(),
         staticFields,
         instanceFields,
         directMethods,
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index af11559..091325a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1280,6 +1280,7 @@
     public int verificationSizeLimitInBytesOverride = -1;
     public boolean forceIRForCfToCfDesugar =
         System.getProperty("com.android.tools.r8.forceIRForCfToCfDesugar") != null;
+    public boolean disableMappingToOriginalProgramVerification = false;
 
     // Flag to allow processing of resources in D8. A data resource consumer still needs to be
     // specified.
diff --git a/src/main/java/com/android/tools/r8/utils/PredicateUtils.java b/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
index 880da5e..f219259 100644
--- a/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils;
 
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 public class PredicateUtils {
@@ -20,4 +21,8 @@
   public static <T> Predicate<T> not(Predicate<T> predicate) {
     return t -> !predicate.test(t);
   }
+
+  public static <T, R> Predicate<T> isNull(Function<T, R> func) {
+    return t -> func.apply(t) == null;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 4baa16c..b3ff5a3 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.bootstrap;
 
+import static com.android.tools.r8.graph.GenericSignatureIdentityTest.testParseSignaturesInJar;
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
 import static com.google.common.io.ByteStreams.toByteArray;
 import static org.hamcrest.CoreMatchers.anyOf;
@@ -208,6 +209,11 @@
   }
 
   @Test
+  public void testSignatures() throws Exception {
+    testParseSignaturesInJar(r8R8Release.getFirst());
+  }
+
+  @Test
   public void test() throws Exception {
     expectThrowsWithHorizontalClassMerging();
     Path helloJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index b567056..b134ae5 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.LazyLoadedDexApplication;
@@ -112,6 +113,7 @@
             Collections.emptyList(),
             null,
             Collections.emptyList(),
+            ClassSignature.NO_CLASS_SIGNATURE,
             DexAnnotationSet.empty(),
             DexEncodedField.EMPTY_ARRAY,
             DexEncodedField.EMPTY_ARRAY,
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index 366e98b..ddff057 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -43,23 +43,28 @@
 @RunWith(Parameterized.class)
 public class GenericSignatureTest extends TestBase {
 
+  private final TestParameters parameters;
+
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withNoneRuntime().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
-  public GenericSignatureTest(TestParameters parameters) {}
+  public GenericSignatureTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
 
   @Test
   public void test() throws Exception {
     AndroidApp app =
-        testForD8()
+        testForD8(parameters.getBackend())
             .debug()
             .addProgramClassesAndInnerClasses(
                 GenericSignatureTestClassA.class,
                 GenericSignatureTestClassB.class,
                 GenericSignatureTestClassCY.class,
                 GenericSignatureTestClassCYY.class)
+            .setMinApi(parameters.getApiLevel())
             .compile()
             .app;
     AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(app);
@@ -103,13 +108,7 @@
     // class <T:GenericSignatureTestClassA<T>.Y>CYY<T extends A<T>.Y> extends CY<T>
     DexClass clazz = cyy.getDexProgramClass();
     assertNotNull(clazz);
-    classSignature =
-        GenericSignature.parseClassSignature(
-            clazz.getType().getName(),
-            getGenericSignature(clazz, appView),
-            clazz.origin,
-            appView.dexItemFactory(),
-            appView.options().reporter);
+    classSignature = clazz.classSignature;
     assertNotNull(classSignature);
 
     assertEquals(1, classSignature.formalTypeParameters.size());
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
index e11a02a..fade8ed 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
 import static com.android.tools.r8.graph.GenericSignature.ClassSignature.NO_CLASS_SIGNATURE;
+import static com.google.common.base.Predicates.alwaysFalse;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
@@ -155,7 +156,7 @@
         GenericSignature.parseClassSignature(
             "A", signature, Origin.unknown(), new DexItemFactory(), new Reporter());
     GenericSignaturePrinter genericSignaturePrinter =
-        new GenericSignaturePrinter(NamingLens.getIdentityLens());
+        new GenericSignaturePrinter(NamingLens.getIdentityLens(), alwaysFalse());
     genericSignaturePrinter.visitClassSignature(parsed);
     String outSignature = genericSignaturePrinter.toString();
     assertEquals(signature, outSignature);
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
index 0465bc5..99b186f 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -35,6 +36,7 @@
           Collections.emptyList(),
           null,
           Collections.emptyList(),
+          ClassSignature.NO_CLASS_SIGNATURE,
           DexAnnotationSet.empty(),
           DexEncodedField.EMPTY_ARRAY,
           DexEncodedField.EMPTY_ARRAY,
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 e03e056..fd09ef3 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -46,6 +46,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.InitClassLens;
 import com.android.tools.r8.graph.MethodAccessFlags;
@@ -830,6 +831,7 @@
               Collections.emptyList(),
               null,
               Collections.emptyList(),
+              ClassSignature.NO_CLASS_SIGNATURE,
               DexAnnotationSet.empty(),
               DexEncodedField.EMPTY_ARRAY,
               DexEncodedField.EMPTY_ARRAY,
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java b/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
index b2ec374..e3e965c 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
@@ -322,6 +322,9 @@
                 ProguardKeepAttributes.SIGNATURE)
             .addKeepAllClassesRuleWithAllowObfuscation()
             .allowDiagnosticMessages()
+            .addOptionsModification(
+                internalOptions ->
+                    internalOptions.testing.disableMappingToOriginalProgramVerification = true)
             .compile();
 
     compileResult.assertNoInfoMessages();
diff --git a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
index 01a95ba..7511e28 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
@@ -58,7 +58,7 @@
             L.class,
             ImplL.class)
         .addKeepMainRule(Main.class)
-        .addKeepClassRules(InterfaceToKeep.class)
+        .addKeepClassRules(InterfaceToKeep.class, ImplI.class, K.class)
         .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*")
         .setMinApi(parameters.getApiLevel())
         .noMinification()
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 6a5b949..d1e934f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -245,12 +245,10 @@
   }
 
   public String getOriginalSignatureAttribute(
-      DexAnnotationSet annotations, BiConsumer<GenericSignatureParser, String> parse) {
-    String finalSignature = getFinalSignatureAttribute(annotations);
+      String finalSignature, BiConsumer<GenericSignatureParser, String> parse) {
     if (finalSignature == null || mapping == null) {
       return finalSignature;
     }
-
     GenericSignatureGenerator rewriter = new GenericSignatureGenerator();
     GenericSignatureParser<String> parser = new GenericSignatureParser<>(rewriter);
     parse.accept(parser, finalSignature);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index ab1d03b..8e961c7 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -359,12 +359,12 @@
   @Override
   public String getOriginalSignatureAttribute() {
     return codeInspector.getOriginalSignatureAttribute(
-        dexClass.annotations(), GenericSignatureParser::parseClassSignature);
+        dexClass.getClassSignature().toString(), GenericSignatureParser::parseClassSignature);
   }
 
   @Override
   public String getFinalSignatureAttribute() {
-    return codeInspector.getFinalSignatureAttribute(dexClass.annotations());
+    return dexClass.getClassSignature().toString();
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index 411dad2..9ef7938 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -134,7 +134,8 @@
   @Override
   public String getOriginalSignatureAttribute() {
     return codeInspector.getOriginalSignatureAttribute(
-        dexField.annotations(), GenericSignatureParser::parseFieldSignature);
+        codeInspector.getFinalSignatureAttribute(dexField.annotations()),
+        GenericSignatureParser::parseFieldSignature);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 536aa5e3..4e49582 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -186,7 +186,8 @@
   @Override
   public String getOriginalSignatureAttribute() {
     return codeInspector.getOriginalSignatureAttribute(
-        dexMethod.annotations(), GenericSignatureParser::parseMethodSignature);
+        codeInspector.getFinalSignatureAttribute(dexMethod.annotations()),
+        GenericSignatureParser::parseMethodSignature);
   }
 
   public DexMethod getOriginalDexMethod(DexItemFactory dexItemFactory) {