First step towards internal encoding of generic signature.

Kotlin @Metadata rewriting needs to retrieve specific type parameters
from Signature attributes. We have String-to-String signature rewriter,
but we really need our own encodings that conform to syntax of JVM
Signature attributes in order to retrieve specific type parameters for
specific arguments.

To that end, this CL introduces R8 internal encodings for Signature
(in part), along with the parser. The parser is technically same as
existing GenericSignatureRewriter, with some modification to output
parsed encoding at each parsing level.

Bug: 70169921, 129925954
Change-Id: I73cefdbe563514bb87f35e20e0981c868ba5d09b
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
new file mode 100644
index 0000000..f948208
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -0,0 +1,941 @@
+// 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.utils.DescriptorUtils.getClassBinaryNameFromDescriptor;
+import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
+
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.GenericSignatureFormatError;
+import java.nio.CharBuffer;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Internal encoding of the generics signature attribute as defined by JVMS 7 $ 4.3.4.
+ * <pre>
+ * ClassSignature ::=
+ *     FormalTypeParameters? SuperclassSignature SuperinterfaceSignature*
+ *
+ *
+ * FormalTypeParameters ::=
+ *     < FormalTypeParameter+ >
+ *
+ * FormalTypeParameter ::=
+ *     Identifier ClassBound InterfaceBound*
+ *
+ * ClassBound ::=
+ *     : FieldTypeSignature?
+ *
+ * InterfaceBound ::=
+ *     : FieldTypeSignature
+ *
+ * SuperclassSignature ::=
+ *     ClassTypeSignature
+ *
+ * SuperinterfaceSignature ::=
+ *     ClassTypeSignature
+ *
+ *
+ * FieldTypeSignature ::=
+ *     ClassTypeSignature
+ *     ArrayTypeSignature
+ *     TypeVariableSignature
+ *
+ *
+ * ClassTypeSignature ::=
+ *     L PackageSpecifier? SimpleClassTypeSignature ClassTypeSignatureSuffix* ;
+ *
+ * PackageSpecifier ::=
+ *     Identifier / PackageSpecifier*
+ *
+ * SimpleClassTypeSignature ::=
+ *     Identifier TypeArguments?
+ *
+ * ClassTypeSignatureSuffix ::=
+ *     . SimpleClassTypeSignature
+ *
+ * TypeVariableSignature ::=
+ *     T Identifier ;
+ *
+ * TypeArguments ::=
+ *     < TypeArgument+ >
+ *
+ * TypeArgument ::=
+ *     WildcardIndicator? FieldTypeSignature
+ *     *
+ *
+ * WildcardIndicator ::=
+ *     +
+ *     -
+ *
+ * ArrayTypeSignature ::=
+ *     [ TypeSignature
+ *
+ * TypeSignature ::=
+ *     FieldTypeSignature
+ *     BaseType
+ *
+ *
+ * MethodTypeSignature ::=
+ *     FormalTypeParameters? (TypeSignature*) ReturnType ThrowsSignature*
+ *
+ * ReturnType ::=
+ *     TypeSignature
+ *     VoidDescriptor
+ *
+ * ThrowsSignature ::=
+ *     ^ ClassTypeSignature
+ *     ^ TypeVariableSignature
+ * </pre>
+ */
+public class GenericSignature {
+
+  interface DexDefinitionSignature<T extends DexDefinition> {
+    default boolean isClassSignature() {
+      return false;
+    }
+
+    default ClassSignature asClassSignature() {
+      return null;
+    }
+
+    default boolean isFieldTypeSignature() {
+      return false;
+    }
+
+    default FieldTypeSignature asFieldTypeSignature() {
+      return null;
+    }
+
+    default boolean isMethodTypeSignature() {
+      return false;
+    }
+
+    default MethodTypeSignature asMethodTypeSignature() {
+      return null;
+    }
+  }
+
+  public static class ClassSignature implements DexDefinitionSignature<DexClass> {
+    static final ClassSignature UNKNOWN_CLASS_SIGNATURE =
+        new ClassSignature(ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE, ImmutableList.of());
+
+    // TODO(b/129925954): encoding formal type parameters
+    final ClassTypeSignature superClassSignature;
+    final List<ClassTypeSignature> superInterfaceSignatures;
+
+    ClassSignature(
+        ClassTypeSignature superClassSignature,
+        List<ClassTypeSignature> superInterfaceSignatures) {
+      this.superClassSignature = superClassSignature;
+      this.superInterfaceSignatures = superInterfaceSignatures;
+    }
+
+    public ClassTypeSignature superClassSignature() {
+      return superClassSignature;
+    }
+
+    public List<ClassTypeSignature> superInterfaceSignatures() {
+      return superInterfaceSignatures;
+    }
+
+    @Override
+    public boolean isClassSignature() {
+      return true;
+    }
+
+    @Override
+    public ClassSignature asClassSignature() {
+      return this;
+    }
+  }
+
+  public abstract static class TypeSignature {
+    public boolean isFieldTypeSignature() {
+      return false;
+    }
+
+    public FieldTypeSignature asFieldTypeSignature() {
+      return null;
+    }
+
+    public boolean isBaseTypeSignature() {
+      return false;
+    }
+
+    public BaseTypeSignature asBaseTypeSignature() {
+      return null;
+    }
+
+    public abstract TypeSignature toArrayTypeSignature(AppView<?> appView);
+
+    public abstract TypeSignature toArrayElementTypeSignature(AppView<?> appView);
+  }
+
+  // TODO(b/129925954): better structures for a circle of
+  //  TypeSignature - FieldTypeSignature - ClassTypeSignature - TypeArgument
+  public abstract static class FieldTypeSignature
+      extends TypeSignature implements DexDefinitionSignature<DexEncodedField> {
+    @Override
+    public boolean isFieldTypeSignature() {
+      return true;
+    }
+
+    @Override
+    public FieldTypeSignature asFieldTypeSignature() {
+      return this;
+    }
+
+    public boolean isClassTypeSignature() {
+      return false;
+    }
+
+    public ClassTypeSignature asClassTypeSignature() {
+      return null;
+    }
+
+    public boolean isTypeVariableSignature() {
+      return false;
+    }
+
+    public TypeVariableSignature asTypeVariableSignature() {
+      return null;
+    }
+  }
+
+  // TODO(b/129925954): separate ArrayTypeSignature or just reuse ClassTypeSignature?
+  public static class ClassTypeSignature extends FieldTypeSignature {
+    static final ClassTypeSignature UNKNOWN_CLASS_TYPE_SIGNATURE =
+        new ClassTypeSignature(null, ImmutableList.of());
+
+    // This covers class type or array type, with or without type arguments.
+    final DexType type;
+    // E.g., for Map<K, V>, a signature will indicate what types are for K and V.
+    // Note that this could be nested, e.g., Map<K, Consumer<V>>.
+    // TODO(b/129925954): What about * ?
+    final List<FieldTypeSignature> typeArguments;
+
+    // TODO(b/129925954): towards immutable structure?
+    // Double-linked enclosing-inner relations.
+    ClassTypeSignature enclosingTypeSignature;
+    ClassTypeSignature innerTypeSignature;
+
+    ClassTypeSignature(DexType type, List<FieldTypeSignature> typeArguments) {
+      this.type = type;
+      this.typeArguments = typeArguments;
+    }
+
+    public DexType type() {
+      return type;
+    }
+
+    public List<FieldTypeSignature> typeArguments() {
+      return typeArguments;
+    }
+
+    @Override
+    public boolean isClassTypeSignature() {
+      return true;
+    }
+
+    @Override
+    public ClassTypeSignature asClassTypeSignature() {
+      return this;
+    }
+
+    @Override
+    public ClassTypeSignature toArrayTypeSignature(AppView<?> appView) {
+      DexType arrayType = type.toArrayType(1, appView.dexItemFactory());
+      ClassTypeSignature result = new ClassTypeSignature(arrayType, typeArguments);
+      copyEnclosingRelations(result);
+      return result;
+    }
+
+    @Override
+    public ClassTypeSignature toArrayElementTypeSignature(AppView<?> appView) {
+      assert type.isArrayType();
+      DexType elementType = type.toArrayElementType( appView.dexItemFactory());
+      ClassTypeSignature result = new ClassTypeSignature(elementType, typeArguments);
+      copyEnclosingRelations(result);
+      return result;
+    }
+
+    private void copyEnclosingRelations(ClassTypeSignature cloned) {
+      cloned.enclosingTypeSignature = this.enclosingTypeSignature;
+      cloned.innerTypeSignature = this.innerTypeSignature;
+    }
+
+    static void link(ClassTypeSignature outer, ClassTypeSignature inner) {
+      assert outer.innerTypeSignature == null && inner.enclosingTypeSignature == null;
+      outer.innerTypeSignature = inner;
+      inner.enclosingTypeSignature = outer;
+    }
+  }
+
+  public static class TypeVariableSignature extends FieldTypeSignature {
+    final String typeVariable;
+
+    TypeVariableSignature(String typeVariable) {
+      this.typeVariable = typeVariable;
+    }
+
+    @Override
+    public boolean isTypeVariableSignature() {
+      return true;
+    }
+
+    @Override
+    public TypeVariableSignature asTypeVariableSignature() {
+      return this;
+    }
+
+    @Override
+    public TypeSignature toArrayTypeSignature(AppView<?> appView) {
+      throw new Unimplemented("TypeVariableSignature::toArrayTypeSignature");
+    }
+
+    @Override
+    public TypeSignature toArrayElementTypeSignature(AppView<?> appView) {
+      throw new Unimplemented("TypeVariableSignature::toArrayElementTypeSignature");
+    }
+  }
+
+  // TODO(b/129925954): Canonicalization?
+  public static class BaseTypeSignature extends TypeSignature {
+    final DexType type;
+
+    BaseTypeSignature(DexType type) {
+      assert type.isPrimitiveType() || type.isPrimitiveArrayType()
+              || type.isVoidType()
+          : type.toDescriptorString();
+      this.type = type;
+    }
+
+    @Override
+    public boolean isBaseTypeSignature() {
+      return true;
+    }
+
+    @Override
+    public BaseTypeSignature asBaseTypeSignature() {
+      return this;
+    }
+
+    @Override
+    public BaseTypeSignature toArrayTypeSignature(AppView<?> appView) {
+      assert !type.isVoidType();
+      DexType arrayType = type.toArrayType(1, appView.dexItemFactory());
+      return new BaseTypeSignature(arrayType);
+    }
+
+    @Override
+    public BaseTypeSignature toArrayElementTypeSignature(AppView<?> appView) {
+      assert type.isPrimitiveArrayType();
+      DexType elementType = type.toArrayElementType(appView.dexItemFactory());
+      return new BaseTypeSignature(elementType);
+    }
+  }
+
+  public static class MethodTypeSignature implements DexDefinitionSignature<DexEncodedMethod> {
+    static final MethodTypeSignature UNKNOWN_METHOD_TYPE_SIGNATURE =
+        new MethodTypeSignature(
+            ImmutableList.of(),
+            ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE,
+            ImmutableList.of());
+
+    // TODO(b/129925954): encoding formal type parameters
+    final List<TypeSignature> typeSignatures;
+    final TypeSignature returnType;
+    final List<TypeSignature> throwsSignatures;
+
+    MethodTypeSignature(
+        List<TypeSignature> typeSignatures,
+        TypeSignature returnType,
+        List<TypeSignature> throwsSignatures) {
+      this.typeSignatures = typeSignatures;
+      this.returnType = returnType;
+      this.throwsSignatures = throwsSignatures;
+    }
+
+    public TypeSignature getParameterTypeSignature(int i) {
+      if (typeSignatures.isEmpty() || i < 0 || i >= typeSignatures.size()) {
+        return null;
+      }
+      return typeSignatures.get(i);
+    }
+
+    public TypeSignature returnType() {
+      return returnType;
+    }
+
+    public List<TypeSignature> throwsSignatures() {
+      return throwsSignatures;
+    }
+
+    @Override
+    public boolean isMethodTypeSignature() {
+      return true;
+    }
+
+    @Override
+    public MethodTypeSignature asMethodTypeSignature() {
+      return this;
+    }
+  }
+
+  enum Kind {
+    CLASS, FIELD, METHOD;
+
+    static Kind fromDexDefinition(DexDefinition definition) {
+      if (definition.isDexClass()) {
+        return CLASS;
+      }
+      if (definition.isDexEncodedField()) {
+        return FIELD;
+      }
+      if (definition.isDexEncodedMethod()) {
+        return METHOD;
+      }
+      throw new Unreachable("Unexpected kind of DexDefinition: " + definition);
+    }
+
+    Function<String, ? extends DexDefinitionSignature<? extends DexDefinition>>
+        parserMethod(Parser parser) {
+      switch (this) {
+        case CLASS:
+          return parser::parseClassSignature;
+        case FIELD:
+          return parser::parseFieldTypeSignature;
+        case METHOD:
+          return parser::parseMethodTypeSignature;
+      }
+      throw new Unreachable("Unexpected kind: " + this);
+    }
+  }
+
+  public static class Parser {
+    // TODO(b/129925954): Can we merge variants of to*Signature below and just expose
+    //  type-parameterized version of this, like
+    //    <T extends DexDefinitionSignature<?>> T toGenericSignature
+    //  without unchecked cast?
+    private static DexDefinitionSignature<? extends DexDefinition> toGenericSignature(
+        DexClass currentClassContext,
+        DexDefinition definition,
+        AppView<AppInfoWithLiveness> appView) {
+      DexAnnotationSet annotations = definition.annotations();
+      if (annotations.annotations.length == 0) {
+        return null;
+      }
+      for (int i = 0; i < annotations.annotations.length; i++) {
+        DexAnnotation annotation = annotations.annotations[i];
+        if (!DexAnnotation.isSignatureAnnotation(annotation, appView.dexItemFactory())) {
+          continue;
+        }
+        Kind kind = Kind.fromDexDefinition(definition);
+        Parser parser = new Parser(currentClassContext, appView);
+        String signature = DexAnnotation.getSignature(annotation);
+        try {
+          return kind.parserMethod(parser).apply(signature);
+        } catch (GenericSignatureFormatError e) {
+          appView.options().warningInvalidSignature(
+              definition, currentClassContext.getOrigin(), signature, e);
+        }
+      }
+      return null;
+    }
+
+    public static ClassSignature toClassSignature(
+        DexClass clazz, AppView<AppInfoWithLiveness> appView) {
+      DexDefinitionSignature<?> signature = toGenericSignature(clazz, clazz, appView);
+      if (signature != null) {
+        assert signature.isClassSignature();
+        return signature.asClassSignature();
+      }
+      return ClassSignature.UNKNOWN_CLASS_SIGNATURE;
+    }
+
+    public static FieldTypeSignature toFieldTypeSignature(
+        DexEncodedField field, AppView<AppInfoWithLiveness> appView) {
+      DexClass currentClassContext = appView.definitionFor(field.field.holder);
+      DexDefinitionSignature<?> signature =
+          toGenericSignature(currentClassContext, field, appView);
+      if (signature != null) {
+        assert signature.isFieldTypeSignature();
+        return signature.asFieldTypeSignature();
+      }
+      return ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
+    }
+
+    public static MethodTypeSignature toMethodTypeSignature(
+        DexEncodedMethod method, AppView<AppInfoWithLiveness> appView) {
+      DexClass currentClassContext = appView.definitionFor(method.method.holder);
+      DexDefinitionSignature<?> signature =
+          toGenericSignature(currentClassContext, method, appView);
+      if (signature != null) {
+        assert signature.isMethodTypeSignature();
+        return signature.asMethodTypeSignature();
+      }
+      return MethodTypeSignature.UNKNOWN_METHOD_TYPE_SIGNATURE;
+    }
+
+    /*
+     * Parser:
+     */
+    private char symbol; // 0: eof; else valid term symbol or first char of identifier.
+
+    private String identifier;
+
+    /*
+     * Scanner:
+     * eof is private to the scan methods
+     * and it's set only when a scan is issued at the end of the buffer.
+     */
+    private boolean eof;
+
+    private char[] buffer;
+
+    private int pos;
+
+    private Parser(DexClass currentClassContext, AppView<AppInfoWithLiveness> appView) {
+      this.currentClassContext = currentClassContext;
+      this.appView = appView;
+    }
+
+    ClassSignature parseClassSignature(String signature) {
+      try {
+        setInput(signature);
+        return parseClassSignature();
+      } catch (Unimplemented e) {
+        // TODO(b/129925954): Should not catch this once fully implemented
+        return ClassSignature.UNKNOWN_CLASS_SIGNATURE;
+      } catch (GenericSignatureFormatError e) {
+        throw e;
+      } catch (Throwable t) {
+        Error e = new GenericSignatureFormatError(
+            "Unknown error parsing class signature: " + t.getMessage());
+        e.addSuppressed(t);
+        throw e;
+      }
+    }
+
+    MethodTypeSignature parseMethodTypeSignature(String signature) {
+      try {
+        setInput(signature);
+        return parseMethodTypeSignature();
+      } catch (Unimplemented e) {
+        // TODO(b/129925954): Should not catch this once fully implemented
+        return MethodTypeSignature.UNKNOWN_METHOD_TYPE_SIGNATURE;
+      } catch (GenericSignatureFormatError e) {
+        throw e;
+      } catch (Throwable t) {
+        Error e = new GenericSignatureFormatError(
+            "Unknown error parsing method signature: " + t.getMessage());
+        e.addSuppressed(t);
+        throw e;
+      }
+    }
+
+    FieldTypeSignature parseFieldTypeSignature(String signature) {
+      try {
+        setInput(signature);
+        return parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+      } catch (Unimplemented e) {
+        // TODO(b/129925954): Should not catch this once fully implemented
+        return ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
+      } catch (GenericSignatureFormatError e) {
+        throw e;
+      } catch (Throwable t) {
+        Error e = new GenericSignatureFormatError(
+            "Unknown error parsing field signature: " + t.getMessage());
+        e.addSuppressed(t);
+        throw e;
+      }
+    }
+
+    private void setInput(String input) {
+      this.buffer = input.toCharArray();
+      this.eof = false;
+      pos = 0;
+      symbol = 0;
+      identifier = null;
+      scanSymbol();
+    }
+
+    //
+    // Action:
+    //
+
+    enum ParserPosition {
+      CLASS_SUPER_OR_INTERFACE_ANNOTATION,
+      ENCLOSING_INNER_OR_TYPE_ANNOTATION,
+      MEMBER_ANNOTATION
+    }
+
+    private final AppView<AppInfoWithLiveness> appView;
+    private final DexClass currentClassContext;
+    private DexType lastWrittenType = null;
+
+    private DexType parsedTypeName(String name, ParserPosition parserPosition) {
+      if (parserPosition == ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION
+          && lastWrittenType == null) {
+        // We are writing type-arguments for a merged class.
+        return null;
+      }
+      String originalDescriptor = getDescriptorFromClassBinaryName(name);
+      DexType type =
+          appView.graphLense().lookupType(appView.dexItemFactory().createType(originalDescriptor));
+      if (appView.appInfo().wasPruned(type)) {
+        type = appView.dexItemFactory().objectType;
+      }
+      if (parserPosition == ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION
+          && currentClassContext != null) {
+        // We may have merged the type down to the current class type.
+        DexString classDescriptor = currentClassContext.type.descriptor;
+        if (!originalDescriptor.equals(classDescriptor.toString())
+            && type.descriptor.equals(classDescriptor)) {
+          lastWrittenType = null;
+          return type;
+        }
+      }
+      lastWrittenType = type;
+      return type;
+    }
+
+    private DexType parsedInnerTypeName(DexType enclosingType, String name) {
+      if (enclosingType == null) {
+        // We are writing inner type names
+        return null;
+      }
+      assert enclosingType.isClassType();
+      String enclosingDescriptor = enclosingType.toDescriptorString();
+      DexType type =
+          appView
+              .dexItemFactory()
+              .createType(
+                  getDescriptorFromClassBinaryName(
+                      getClassBinaryNameFromDescriptor(enclosingDescriptor)
+                          + DescriptorUtils.INNER_CLASS_SEPARATOR
+                          + name));
+      return appView.graphLense().lookupType(type);
+    }
+
+    //
+    // Parser:
+    //
+
+    private ClassSignature parseClassSignature() {
+      // ClassSignature ::= FormalTypeParameters? SuperclassSignature SuperinterfaceSignature*.
+
+      parseOptFormalTypeParameters();
+
+      // SuperclassSignature ::= ClassTypeSignature.
+      ClassTypeSignature superClassSignature =
+          parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION);
+
+      ImmutableList.Builder<ClassTypeSignature> builder = ImmutableList.builder();
+      while (symbol > 0) {
+        // SuperinterfaceSignature ::= ClassTypeSignature.
+        builder.add(parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION));
+      }
+
+      return new ClassSignature(superClassSignature, builder.build());
+    }
+
+    private void parseOptFormalTypeParameters() {
+      // FormalTypeParameters ::= "<" FormalTypeParameter+ ">".
+
+      if (symbol == '<') {
+        scanSymbol();
+
+        updateFormalTypeParameter();
+
+        while ((symbol != '>') && (symbol > 0)) {
+          updateFormalTypeParameter();
+        }
+
+        expect('>');
+      }
+    }
+
+    private void updateFormalTypeParameter() {
+      // FormalTypeParameter ::= Identifier ClassBound InterfaceBound*.
+      scanIdentifier();
+      assert identifier != null;
+
+      // ClassBound ::= ":" FieldTypeSignature?.
+      expect(':');
+
+      if (symbol == 'L' || symbol == '[' || symbol == 'T') {
+        parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+      }
+
+      while (symbol == ':') {
+        // InterfaceBound ::= ":" FieldTypeSignature.
+        scanSymbol();
+        parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+      }
+    }
+
+    private FieldTypeSignature parseFieldTypeSignature(ParserPosition parserPosition) {
+      // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
+      switch (symbol) {
+        case 'L':
+          return parseClassTypeSignature(parserPosition);
+        case '[':
+          // ArrayTypeSignature ::= "[" TypeSignature.
+          scanSymbol();
+          TypeSignature baseTypeSignature = updateTypeSignature(parserPosition);
+          return baseTypeSignature.toArrayTypeSignature(appView).asFieldTypeSignature();
+        case 'T':
+          return updateTypeVariableSignature();
+        default:
+          parseError("Expected L, [ or T", pos);
+      }
+      throw new Unreachable("Either FieldTypeSignature is returned or a parse error is thrown.");
+    }
+
+    private ClassTypeSignature parseClassTypeSignature(ParserPosition parserPosition) {
+      // ClassTypeSignature ::=
+      //   "L" (Identifier "/")* Identifier TypeArguments? ("." Identifier TypeArguments?)* ";".
+      expect('L');
+
+      StringBuilder qualIdent = new StringBuilder();
+      scanIdentifier();
+      assert identifier != null;
+      while (symbol == '/') {
+        qualIdent.append(identifier).append(symbol);
+        scanSymbol();
+        scanIdentifier();
+        assert identifier != null;
+      }
+
+      qualIdent.append(this.identifier);
+      DexType parsedEnclosingType = parsedTypeName(qualIdent.toString(), parserPosition);
+
+      List<FieldTypeSignature> typeArguments = updateOptTypeArguments();
+      ClassTypeSignature outerMostTypeSignature =
+          new ClassTypeSignature(parsedEnclosingType, typeArguments);
+
+      ClassTypeSignature outerTypeSignature = outerMostTypeSignature;
+      ClassTypeSignature innerTypeSignature;
+      while (symbol == '.') {
+        // Deal with Member Classes.
+        scanSymbol();
+        scanIdentifier();
+        assert identifier != null;
+        parsedEnclosingType = parsedInnerTypeName(parsedEnclosingType, identifier);
+        typeArguments = updateOptTypeArguments();
+        innerTypeSignature = new ClassTypeSignature(parsedEnclosingType, typeArguments);
+        ClassTypeSignature.link(outerTypeSignature, innerTypeSignature);
+        outerTypeSignature = innerTypeSignature;
+      }
+
+      expect(';');
+      return outerMostTypeSignature;
+    }
+
+    private List<FieldTypeSignature> updateOptTypeArguments() {
+      ImmutableList.Builder<FieldTypeSignature> builder = ImmutableList.builder();
+      // OptTypeArguments ::= "<" TypeArgument+ ">".
+      if (symbol == '<') {
+        scanSymbol();
+
+        builder.add(updateTypeArgument());
+        while ((symbol != '>') && (symbol > 0)) {
+          builder.add(updateTypeArgument());
+        }
+
+        expect('>');
+      }
+      return builder.build();
+    }
+
+    private FieldTypeSignature updateTypeArgument() {
+      // TypeArgument ::= (["+" | "-"] FieldTypeSignature) | "*".
+      if (symbol == '*') {
+        scanSymbol();
+        throw new Unimplemented("GenericSignature.TypeArgument *");
+      } else if (symbol == '+') {
+        scanSymbol();
+        return parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
+      } else if (symbol == '-') {
+        scanSymbol();
+        return parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
+      } else {
+        return parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
+      }
+    }
+
+    private TypeVariableSignature updateTypeVariableSignature() {
+      // TypeVariableSignature ::= "T" Identifier ";".
+      expect('T');
+
+      scanIdentifier();
+      assert identifier != null;
+
+      expect(';');
+      return new TypeVariableSignature(identifier);
+    }
+
+    private TypeSignature updateTypeSignature(ParserPosition parserPosition) {
+      switch (symbol) {
+        case 'B':
+        case 'C':
+        case 'D':
+        case 'F':
+        case 'I':
+        case 'J':
+        case 'S':
+        case 'Z':
+          DexType type = appView.dexItemFactory().createType(String.valueOf(symbol));
+          BaseTypeSignature baseTypeSignature = new BaseTypeSignature(type);
+          scanSymbol();
+          return baseTypeSignature;
+        default:
+          // Not an elementary type, but a FieldTypeSignature.
+          return parseFieldTypeSignature(parserPosition);
+      }
+    }
+
+    private MethodTypeSignature parseMethodTypeSignature() {
+      // MethodTypeSignature ::=
+      //     FormalTypeParameters? "(" TypeSignature* ")" ReturnType ThrowsSignature*.
+      parseOptFormalTypeParameters();
+
+      expect('(');
+
+      ImmutableList.Builder<TypeSignature> parameterSignatureBuilder = ImmutableList.builder();
+      while (symbol != ')' && (symbol > 0)) {
+        parameterSignatureBuilder.add(updateTypeSignature(ParserPosition.MEMBER_ANNOTATION));
+      }
+
+      expect(')');
+
+      TypeSignature returnType = updateReturnType();
+
+      ImmutableList.Builder<TypeSignature> throwsSignatureBuilder = ImmutableList.builder();
+      if (symbol == '^') {
+        do {
+          scanSymbol();
+
+          // ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
+          if (symbol == 'T') {
+            throwsSignatureBuilder.add(updateTypeVariableSignature());
+          } else {
+            throwsSignatureBuilder.add(parseClassTypeSignature(ParserPosition.MEMBER_ANNOTATION));
+          }
+        } while (symbol == '^');
+      }
+
+      return new MethodTypeSignature(
+          parameterSignatureBuilder.build(), returnType, throwsSignatureBuilder.build());
+    }
+
+    private TypeSignature updateReturnType() {
+      // ReturnType ::= TypeSignature | "V".
+      if (symbol != 'V') {
+        return updateTypeSignature(ParserPosition.MEMBER_ANNOTATION);
+      } else {
+        scanSymbol();
+        return new BaseTypeSignature(appView.dexItemFactory().voidType);
+      }
+    }
+
+    //
+    // Scanner:
+    //
+
+    private void scanSymbol() {
+      if (!eof) {
+        assert buffer != null;
+        if (pos < buffer.length) {
+          symbol = buffer[pos];
+          pos++;
+        } else {
+          symbol = 0;
+          eof = true;
+        }
+      } else {
+        parseError("Unexpected end of signature", pos);
+      }
+    }
+
+    private void expect(char c) {
+      if (eof) {
+        parseError("Unexpected end of signature", pos);
+      }
+      if (symbol == c) {
+        scanSymbol();
+      } else {
+        parseError("Expected " + c, pos - 1);
+      }
+    }
+
+    private boolean isStopSymbol(char ch) {
+      switch (ch) {
+        case ':':
+        case '/':
+        case ';':
+        case '<':
+        case '.':
+          return true;
+        default:
+          return false;
+      }
+    }
+
+    // PRE: symbol is the first char of the identifier.
+    // POST: symbol = the next symbol AFTER the identifier.
+    private void scanIdentifier() {
+      if (!eof && pos < buffer.length) {
+        StringBuilder identifierBuilder = new StringBuilder(32);
+        if (!isStopSymbol(symbol)) {
+          identifierBuilder.append(symbol);
+
+          char[] bufferLocal = buffer;
+          assert bufferLocal != null;
+          do {
+            char ch = bufferLocal[pos];
+            if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))
+                || !isStopSymbol(ch)) {
+              identifierBuilder.append(bufferLocal[pos]);
+              pos++;
+            } else {
+              identifier = identifierBuilder.toString();
+              scanSymbol();
+              return;
+            }
+          } while (pos != bufferLocal.length);
+          identifier = identifierBuilder.toString();
+          symbol = 0;
+          eof = true;
+        } else {
+          // Identifier starts with incorrect char.
+          symbol = 0;
+          eof = true;
+          parseError();
+        }
+      } else {
+        parseError("Unexpected end of signature", pos);
+      }
+    }
+
+    private void parseError() {
+      parseError("Unexpected", pos);
+    }
+
+    private void parseError(String message, int pos) {
+      String arrow = CharBuffer.allocate(pos).toString().replace('\0', ' ') + '^';
+      throw new GenericSignatureFormatError(
+          message + " at position " + (pos + 1) + System.lineSeparator()
+              + String.valueOf(buffer) + System.lineSeparator()
+              + arrow);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
index 7cb3506..81c5413 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
@@ -7,6 +7,8 @@
 /**
  * Actions triggered by the generic signature parser.
  */
+// TODO(b/129925954): Deprecate this once ...graph.GenericSignature is ready and rewriter is
+//   reimplemented based on the internal encoding and transformation logic.
 public interface GenericSignatureAction<T> {
 
   enum ParserPosition {
@@ -15,15 +17,15 @@
     MEMBER_ANNOTATION
   }
 
-  public void parsedSymbol(char symbol);
+  void parsedSymbol(char symbol);
 
-  public void parsedIdentifier(String identifier);
+  void parsedIdentifier(String identifier);
 
-  public T parsedTypeName(String name, ParserPosition isTopLevel);
+  T parsedTypeName(String name, ParserPosition isTopLevel);
 
-  public T parsedInnerTypeName(T enclosingType, String name);
+  T parsedInnerTypeName(T enclosingType, String name);
 
-  public void start();
+  void start();
 
-  public void stop();
+  void stop();
 }
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
index 74b17d6..b953586 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
@@ -26,7 +26,7 @@
  *
  * FieldTypeSignature ::=
  *     ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
- * ArrayTypeSignature ::= "[" TypSignature.
+ * ArrayTypeSignature ::= "[" TypeSignature.
  *
  * ClassTypeSignature ::=
  *     "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments} ";".
@@ -49,6 +49,8 @@
  * VoidDescriptor ::= "V".
  * </pre>
  */
+// TODO(b/129925954): Deprecate this once ...graph.GenericSignature is ready and rewriter is
+//   reimplemented based on the internal encoding and transformation logic.
 public class GenericSignatureParser<T> {
 
   private GenericSignatureAction<T> actions;
@@ -136,7 +138,7 @@
   // Parser:
   //
 
-  void parseClassSignature() {
+  private void parseClassSignature() {
     // ClassSignature ::= OptFormalTypeParameters SuperclassSignature {SuperinterfaceSignature}.
 
     parseOptFormalTypeParameters();
@@ -150,7 +152,7 @@
     }
   }
 
-  void parseOptFormalTypeParameters() {
+  private void parseOptFormalTypeParameters() {
     // OptFormalTypeParameters ::= ["<" FormalTypeParameter {FormalTypeParameter} ">"].
 
     if (symbol == '<') {
@@ -168,7 +170,7 @@
     }
   }
 
-  void updateFormalTypeParameter() {
+  private void updateFormalTypeParameter() {
     // FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
     scanIdentifier();
     assert identifier != null;
@@ -279,7 +281,7 @@
     }
   }
 
- private  void updateTypeVariableSignature() {
+  private void updateTypeVariableSignature() {
     // TypeVariableSignature ::= "T" Ident ";".
     actions.parsedSymbol(symbol);
     expect('T');
@@ -384,7 +386,7 @@
     }
   }
 
- private  boolean isStopSymbol(char ch) {
+  private boolean isStopSymbol(char ch) {
     switch (ch) {
       case ':':
       case '/':
@@ -405,7 +407,6 @@
       if (!isStopSymbol(symbol)) {
         identBuf.append(symbol);
 
-        // FINDBUGS
         char[] bufferLocal = buffer;
         assert bufferLocal != null;
         do {
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 67d2fa9..6e88574 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
@@ -10,13 +10,12 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexDefinition;
 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.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -30,10 +29,12 @@
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
+// TODO(b/129925954): Reimplement this by using the internal encoding and transformation logic.
 public class GenericSignatureRewriter {
 
   private final AppView<AppInfoWithLiveness> appView;
   private final Map<DexType, DexString> renaming;
+  private final InternalOptions options;
   private final Reporter reporter;
 
   public GenericSignatureRewriter(AppView<AppInfoWithLiveness> appView) {
@@ -44,7 +45,8 @@
       AppView<AppInfoWithLiveness> appView, Map<DexType, DexString> renaming) {
     this.appView = appView;
     this.renaming = renaming;
-    this.reporter = appView.options().reporter;
+    this.options = appView.options();
+    this.reporter = options.reporter;
   }
 
   public void run(Iterable<? extends DexProgramClass> classes, ExecutorService executorService)
@@ -65,7 +67,8 @@
                   clazz.annotations(),
                   genericSignatureParser::parseClassSignature,
                   genericSignatureCollector::getRenamedSignature,
-                  (signature, e) -> parseError(clazz, clazz.getOrigin(), signature, e)));
+                  (signature, e) ->
+                      options.warningInvalidSignature(clazz, clazz.getOrigin(), signature, e)));
           clazz.forEachField(
               field ->
                   field.setAnnotations(
@@ -73,7 +76,9 @@
                           field.annotations(),
                           genericSignatureParser::parseFieldSignature,
                           genericSignatureCollector::getRenamedSignature,
-                          (signature, e) -> parseError(field, clazz.getOrigin(), signature, e))));
+                          (signature, e) ->
+                              options.warningInvalidSignature(
+                                  field, clazz.getOrigin(), signature, e))));
           clazz.forEachMethod(
               method ->
                   method.setAnnotations(
@@ -81,7 +86,9 @@
                           method.annotations(),
                           genericSignatureParser::parseMethodSignature,
                           genericSignatureCollector::getRenamedSignature,
-                          (signature, e) -> parseError(method, clazz.getOrigin(), signature, e))));
+                          (signature, e) ->
+                              options.warningInvalidSignature(
+                                  method, clazz.getOrigin(), signature, e))));
         },
         executorService
     );
@@ -137,29 +144,6 @@
     return new DexAnnotationSet(prunedAnnotations);
   }
 
-  private void parseError(
-      DexDefinition item, Origin origin, String signature, GenericSignatureFormatError e) {
-    StringBuilder message = new StringBuilder("Invalid signature '");
-    message.append(signature);
-    message.append("' for ");
-    if (item.isDexClass()) {
-      message.append("class ");
-      message.append((item.asDexClass()).getType().toSourceString());
-    } else if (item.isDexEncodedField()) {
-      message.append("field ");
-      message.append(item.toSourceString());
-    } else {
-      assert item.isDexEncodedMethod();
-      message.append("method ");
-      message.append(item.toSourceString());
-    }
-    message.append(".\n");
-    message.append("Signature is ignored and will not be present in the output.\n");
-    message.append("Parser error: ");
-    message.append(e.getMessage());
-    reporter.warning(new StringDiagnostic(message.toString(), origin));
-  }
-
   private class GenericSignatureCollector implements GenericSignatureAction<DexType> {
     private StringBuilder renamedSignature;
     private final DexProgramClass currentClassContext;
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 7ad79d2..8d04c47 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.features.FeatureSplitConfiguration;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -50,6 +51,7 @@
 import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.lang.reflect.GenericSignatureFormatError;
 import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
@@ -841,6 +843,31 @@
     }
   }
 
+  public void warningInvalidSignature(
+      DexDefinition item, Origin origin, String signature, GenericSignatureFormatError e) {
+    StringBuilder message = new StringBuilder("Invalid signature '");
+    message.append(signature);
+    message.append("' for ");
+    if (item.isDexClass()) {
+      message.append("class ");
+      message.append((item.asDexClass()).getType().toSourceString());
+    } else if (item.isDexEncodedField()) {
+      message.append("field ");
+      message.append(item.toSourceString());
+    } else {
+      assert item.isDexEncodedMethod();
+      message.append("method ");
+      message.append(item.toSourceString());
+    }
+    message.append(".");
+    message.append(System.lineSeparator());
+    message.append("Signature is ignored and will not be present in the output.");
+    message.append(System.lineSeparator());
+    message.append("Parser error: ");
+    message.append(e.getMessage());
+    reporter.warning(new StringDiagnostic(message.toString(), origin));
+  }
+
   public boolean printWarnings() {
     boolean printed = false;
     boolean printOutdatedToolchain = false;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 34b7305..ac15d64 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -598,6 +598,12 @@
     return appView;
   }
 
+  protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(AndroidApp app)
+      throws Exception {
+    return computeAppViewWithLiveness(
+        app, factory -> ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})));
+  }
+
   protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
       AndroidApp app, Class<?> mainClass) throws Exception {
     return computeAppViewWithLiveness(
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
new file mode 100644
index 0000000..710e8da
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -0,0 +1,253 @@
+// 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.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+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.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.Parser;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.junit.Test;
+
+public class GenericSignatureTest extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    AndroidApp app =
+        testForD8()
+            .debug()
+            .addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)
+            .compile()
+            .app;
+    AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(app);
+    DexItemFactory factory = appView.dexItemFactory();
+    CodeInspector inspector = new CodeInspector(appView.appInfo().app());
+
+    ClassSubject a = inspector.clazz(A.class);
+    assertThat(a, isPresent());
+    ClassSubject y = inspector.clazz(A.Y.class);
+    assertThat(y, isPresent());
+    ClassSubject yy = inspector.clazz(A.Y.YY.class);
+    assertThat(yy, isPresent());
+    ClassSubject zz = inspector.clazz(A.Y.ZZ.class);
+    assertThat(zz, isPresent());
+    ClassSubject cy = inspector.clazz(CY.class);
+    assertThat(cy, isPresent());
+    ClassSubject cyy = inspector.clazz(CYY.class);
+    assertThat(cyy, isPresent());
+
+    ClassSignature classSignature;
+    ClassTypeSignature classTypeSignature;
+    FieldTypeSignature fieldTypeSignature;
+    MethodTypeSignature methodTypeSignature;
+    List<FieldTypeSignature> typeArguments;
+    FieldTypeSignature typeArgument;
+
+    //
+    // Testing ClassSignature
+    //
+
+    // class CYY<T extends A<T>.Y> extends CY<T>
+    DexClass clazz = cyy.getDexClass();
+    assertNotNull(clazz);
+    classSignature = Parser.toClassSignature(clazz, appView);
+    assertNotNull(classSignature);
+
+    // TODO(b/129925954): test formal type parameter of CYY
+
+    assertTrue(classSignature.superInterfaceSignatures.isEmpty());
+    classTypeSignature = classSignature.superClassSignature;
+    assertEquals(cy.getDexClass().type, classTypeSignature.type);
+    typeArguments = classTypeSignature.typeArguments;
+    assertEquals(1, typeArguments.size());
+    typeArgument = typeArguments.get(0);
+    assertTrue(typeArgument.isTypeVariableSignature());
+    assertEquals("T", typeArgument.asTypeVariableSignature().typeVariable);
+
+    //
+    // Testing FieldTypeSignature
+    //
+
+    FieldSubject yyInZZ = zz.uniqueFieldWithName("yy");
+    assertThat(yyInZZ, isPresent());
+    DexEncodedField field = yyInZZ.getField();
+    assertNotNull(field);
+
+    fieldTypeSignature = Parser.toFieldTypeSignature(field, appView);
+    assertNotNull(fieldTypeSignature);
+
+    // field type: A$Y$YY
+    assertTrue(fieldTypeSignature.isClassTypeSignature());
+    check_A_Y_YY(a, y, yy, fieldTypeSignature.asClassTypeSignature());
+
+    //
+    // Testing MethodTypeSignature
+    //
+
+    // A$Y$YY newYY()
+    MethodSubject newYY = zz.uniqueMethodWithName("newYY");
+    assertThat(newYY, isPresent());
+    DexEncodedMethod method = newYY.getMethod();
+    assertNotNull(method);
+
+    methodTypeSignature = Parser.toMethodTypeSignature(method, appView);
+    assertNotNull(methodTypeSignature);
+
+    assertTrue(methodTypeSignature.typeSignatures.isEmpty());
+
+    // return type: A$Y$YY
+    TypeSignature returnType = methodTypeSignature.returnType();
+    assertTrue(returnType.isFieldTypeSignature());
+    assertTrue(returnType.asFieldTypeSignature().isClassTypeSignature());
+    check_A_Y_YY(a, y, yy, returnType.asFieldTypeSignature().asClassTypeSignature());
+
+    // Function<A$Y$ZZ<TT>, A$Y$YY> convertToYY(Supplier<A$Y$ZZ<TT>>
+    MethodSubject convertToYY = zz.uniqueMethodWithName("convertToYY");
+    assertThat(convertToYY, isPresent());
+    method = convertToYY.getMethod();
+    assertNotNull(method);
+
+    methodTypeSignature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
+    assertNotNull(methodTypeSignature);
+
+    // return type: Function<A$Y$ZZ<TT>, A$Y$YY>
+    returnType = methodTypeSignature.returnType();
+    assertTrue(returnType.isFieldTypeSignature());
+    assertTrue(returnType.asFieldTypeSignature().isClassTypeSignature());
+    classTypeSignature = returnType.asFieldTypeSignature().asClassTypeSignature();
+    DexType functionType =
+        factory.createType(DescriptorUtils.javaTypeToDescriptor(Function.class.getTypeName()));
+    assertEquals(functionType, classTypeSignature.type);
+
+    typeArguments = classTypeSignature.typeArguments;
+    assertEquals(2, typeArguments.size());
+
+    typeArgument = typeArguments.get(0);
+    assertTrue(typeArgument.isClassTypeSignature());
+    check_A_Y_ZZ(a, y, zz, typeArgument.asClassTypeSignature());
+
+    typeArgument = typeArguments.get(1);
+    assertTrue(typeArgument.isClassTypeSignature());
+    check_A_Y_YY(a, y, yy, typeArgument.asClassTypeSignature());
+
+    // type of 1st argument: Supplier<A$Y$ZZ<TT>>
+    assertEquals(1, methodTypeSignature.typeSignatures.size());
+    TypeSignature parameterSignature = methodTypeSignature.getParameterTypeSignature(0);
+    assertNotNull(parameterSignature);
+    assertTrue(parameterSignature.isFieldTypeSignature());
+    assertTrue(parameterSignature.asFieldTypeSignature().isClassTypeSignature());
+    classTypeSignature = parameterSignature.asFieldTypeSignature().asClassTypeSignature();
+    DexType supplierType =
+        factory.createType(DescriptorUtils.javaTypeToDescriptor(Supplier.class.getTypeName()));
+    assertEquals(supplierType, classTypeSignature.type);
+    typeArguments = classTypeSignature.typeArguments;
+    assertEquals(1, typeArguments.size());
+    typeArgument = typeArguments.get(0);
+    assertTrue(typeArgument.isClassTypeSignature());
+    check_A_Y_ZZ(a, y, zz, typeArgument.asClassTypeSignature());
+  }
+
+  private void check_A_Y(ClassSubject a, ClassSubject y, ClassTypeSignature signature) {
+    assertEquals(a.getDexClass().type, signature.type);
+    List<FieldTypeSignature> typeArguments = signature.typeArguments;
+    assertEquals(1, typeArguments.size());
+    FieldTypeSignature typeArgument = typeArguments.get(0);
+    assertTrue(typeArgument.isTypeVariableSignature());
+    assertEquals("T", typeArgument.asTypeVariableSignature().typeVariable);
+    assertEquals(y.getDexClass().type, signature.innerTypeSignature.type);
+  }
+
+  private void check_A_Y_YY(
+      ClassSubject a, ClassSubject y, ClassSubject yy, ClassTypeSignature signature) {
+    check_A_Y(a, y, signature);
+    assertEquals(yy.getDexClass().type, signature.innerTypeSignature.innerTypeSignature.type);
+  }
+
+  private void check_A_Y_ZZ(
+      ClassSubject a, ClassSubject y, ClassSubject zz, ClassTypeSignature signature) {
+    check_A_Y(a, y, signature);
+    ClassTypeSignature innerMost = signature.innerTypeSignature.innerTypeSignature;
+    assertEquals(zz.getDexClass().type, innerMost.type);
+    List<FieldTypeSignature> typeArguments = innerMost.typeArguments;
+    assertEquals(1, typeArguments.size());
+    FieldTypeSignature typeArgument = typeArguments.get(0);
+    assertTrue(typeArgument.isTypeVariableSignature());
+    assertEquals("TT", typeArgument.asTypeVariableSignature().typeVariable);
+  }
+}
+
+//
+// TODO(b/129925954): Once unified, these would be stale comments.
+// Borrowed from ...naming.signature.GenericSignatureRenamingTest
+// and then extended a bit to explore more details, e.g., MethodTypeSignature.
+//
+
+class A<T> {
+  class Y {
+
+    class YY {}
+
+    class ZZ<TT> extends YY {
+      public YY yy;
+
+      YY newYY() {
+        return new YY();
+      }
+
+      Function<ZZ<TT>, YY> convertToYY(Supplier<ZZ<TT>> zzSupplier) {
+        return zz -> {
+          if (System.currentTimeMillis() > 0) {
+            return zzSupplier.get().newYY();
+          } else {
+            return zz.newYY();
+          }
+        };
+      }
+    }
+
+    ZZ<T> zz() {
+      return new ZZ<T>();
+    }
+  }
+
+  class Z extends Y {}
+
+  static class S {}
+
+  Y newY() {
+    return new Y();
+  }
+
+  Z newZ() {
+    return new Z();
+  }
+
+  Y.ZZ<T> newZZ() {
+    return new Y().zz();
+  }
+}
+
+class B<T extends A<T>> {}
+
+class CY<T extends A<T>.Y> {}
+
+class CYY<T extends A<T>.Y> extends CY<T> {}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
index 5b887eb..892b7d2 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
@@ -59,7 +60,9 @@
   private void test(R8TestBuilder<?> builder) throws Exception {
     builder
         .addKeepRules("-dontoptimize")
-        .addKeepRules("-keepattributes InnerClasses,EnclosingMethod,Signature")
+        .addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
+        .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
+        .addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
         .addKeepMainRule(Main.class)
         .addProgramClasses(Main.class)
         .addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)