Towards synthesizing Kotlin @Metadata: extensions.

Bug: 70169921
Change-Id: Ibe19f151f8277777cc79c655de02298bf71f89e0
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 2a87607..65fb712 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -13,20 +13,23 @@
 import com.android.tools.r8.utils.PredicateUtils;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Sets;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmProperty;
 
 public abstract class DexClass extends DexDefinition {
@@ -210,14 +213,33 @@
     return Arrays.asList(virtualMethods);
   }
 
-  public List<DexEncodedMethod> kotlinFunctions(List<KmProperty> kmProperties) {
-    List<DexEncodedMethod> functions = new ArrayList<>();
-    for (DexEncodedMethod method : virtualMethods) {
-      if (method.isKotlinFunction(kmProperties)) {
-        functions.add(method);
+  public Map<DexEncodedMethod, KmFunction> kotlinExtensions(
+      List<KmFunction> extensions, AppView<?> appView) {
+    ImmutableMap.Builder<DexEncodedMethod, KmFunction> builder = ImmutableMap.builder();
+    for (DexEncodedMethod method : directMethods) {
+      KmFunction extension = method.findCompatibleKotlinExtension(extensions, appView);
+      if (extension != null) {
+        // Found a compatible extension that is likely asked to keep.
+        builder.put(method, extension);
       }
     }
-    return functions;
+    return builder.build();
+  }
+
+  public List<DexEncodedMethod> kotlinFunctions(
+      List<KmFunction> functions, List<KmProperty> properties, AppView<?> appView) {
+    ImmutableList.Builder<DexEncodedMethod> builder = ImmutableList.builder();
+    for (DexEncodedMethod method : virtualMethods) {
+      KmFunction function = method.findCompatibleKotlinFunction(functions, appView);
+      if (function != null) {
+        // Found a compatible function that is likely asked to keep.
+        builder.add(method);
+      } else if (!method.isKotlinProperty(properties)) {
+        // This could be a newly merged method that is not part of properties.
+        builder.add(method);
+      }
+    }
+    return builder.build();
   }
 
   public void appendVirtualMethod(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index e4fff90..90fee5d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -54,6 +54,7 @@
 import com.android.tools.r8.ir.synthetic.FieldAccessorSourceCode;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.android.tools.r8.kotlin.KotlinMetadataSynthesizer;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -73,6 +74,7 @@
 import java.util.Map;
 import java.util.function.Consumer;
 import java.util.function.IntPredicate;
+import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmProperty;
 import org.objectweb.asm.Opcodes;
 
@@ -349,8 +351,29 @@
     return accessFlags.isSynthetic();
   }
 
-  boolean isKotlinFunction(List<KmProperty> properties) {
-    return !isStaticMember() && !isKotlinProperty(properties);
+  // TODO(b/70169921): Handling JVM extensions as well.
+  KmFunction findCompatibleKotlinExtension(List<KmFunction> extensions, AppView<?> appView) {
+    if (!isStaticMember()) {
+      return null;
+    }
+    for (KmFunction extension : extensions) {
+      if (KotlinMetadataSynthesizer.isCompatibleExtension(extension, this, appView)) {
+        return extension;
+      }
+    }
+    return null;
+  }
+
+  KmFunction findCompatibleKotlinFunction(List<KmFunction> functions, AppView<?> appView) {
+    if (isStaticMember()) {
+      return null;
+    }
+    for (KmFunction function : functions) {
+      if (KotlinMetadataSynthesizer.isCompatibleFunction(function, this, appView)) {
+        return function;
+      }
+    }
+    return null;
   }
 
   // E.g., property `prop: T` is mapped to `getProp()T`, `setProp(T)V`, `prop$annotations()V`.
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index e76bbcf..2ac086e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -5,8 +5,10 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension;
 import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toKmType;
 import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunction;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunctionAsExtension;
 import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmType;
 
 import com.android.tools.r8.graph.AppView;
@@ -16,6 +18,8 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 import kotlinx.metadata.KmClass;
 import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmProperty;
@@ -66,12 +70,29 @@
     }
 
     List<KmFunction> functions = kmClass.getFunctions();
+    List<KmFunction> originalFunctions =
+        functions.stream()
+            .filter(kmFunction -> !isExtension(kmFunction))
+            .collect(Collectors.toList());
+    List<KmFunction> originalExtensions =
+        functions.stream()
+            .filter(KotlinMetadataSynthesizer::isExtension)
+            .collect(Collectors.toList());
     functions.clear();
+
     List<KmProperty> properties = kmClass.getProperties();
-    for (DexEncodedMethod method : clazz.kotlinFunctions(properties)) {
-      KmFunction kmFunction = toRenamedKmFunction(method.method, appView, lens);
-      if (kmFunction != null) {
-        functions.add(kmFunction);
+    for (DexEncodedMethod method : clazz.kotlinFunctions(originalFunctions, properties, appView)) {
+      KmFunction function = toRenamedKmFunction(method, null, appView, lens);
+      if (function != null) {
+        functions.add(function);
+      }
+    }
+    for (Map.Entry<DexEncodedMethod, KmFunction> entry :
+        clazz.kotlinExtensions(originalExtensions, appView).entrySet()) {
+      KmFunction extension =
+          toRenamedKmFunctionAsExtension(entry.getKey(), entry.getValue(), appView, lens);
+      if (extension != null) {
+        functions.add(extension);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index 2b1f9bc..68b607df 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -4,10 +4,17 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunctionAsExtension;
+
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmPackage;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -37,15 +44,29 @@
 
   @Override
   void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
-    // TODO(b/70169921): no idea yet!
-    assert lens.lookupType(clazz.type, appView.dexItemFactory()) == clazz.type
-        : toString();
+    List<KmFunction> functions = kmPackage.getFunctions();
+    List<KmFunction> originalExtensions =
+        functions.stream()
+            .filter(KotlinMetadataSynthesizer::isExtension)
+            .collect(Collectors.toList());
+    functions.clear();
+
+    for (Map.Entry<DexEncodedMethod, KmFunction> entry :
+        clazz.kotlinExtensions(originalExtensions, appView).entrySet()) {
+      KmFunction extension =
+          toRenamedKmFunctionAsExtension(entry.getKey(), entry.getValue(), appView, lens);
+      if (extension != null) {
+        functions.add(extension);
+      }
+    }
   }
 
   @Override
   KotlinClassHeader createHeader() {
-    // TODO(b/70169921): may need to update if `rewrite` is implemented.
-    return metadata.getHeader();
+    KotlinClassMetadata.MultiFileClassPart.Writer writer =
+        new KotlinClassMetadata.MultiFileClassPart.Writer();
+    kmPackage.accept(writer);
+    return writer.write(metadata.getFacadeClassName()).getHeader();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index e2bb332..e0a3935 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -4,10 +4,17 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmFunctionAsExtension;
+
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import kotlinx.metadata.KmFunction;
 import kotlinx.metadata.KmPackage;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -37,15 +44,28 @@
 
   @Override
   void rewrite(AppView<AppInfoWithLiveness> appView, NamingLens lens) {
-    // TODO(b/70169921): no idea yet!
-    assert lens.lookupType(clazz.type, appView.dexItemFactory()) == clazz.type
-        : toString();
+    List<KmFunction> functions = kmPackage.getFunctions();
+    List<KmFunction> originalExtensions =
+        functions.stream()
+            .filter(KotlinMetadataSynthesizer::isExtension)
+            .collect(Collectors.toList());
+    functions.clear();
+
+    for (Map.Entry<DexEncodedMethod, KmFunction> entry :
+        clazz.kotlinExtensions(originalExtensions, appView).entrySet()) {
+      KmFunction extension =
+          toRenamedKmFunctionAsExtension(entry.getKey(), entry.getValue(), appView, lens);
+      if (extension != null) {
+        functions.add(extension);
+      }
+    }
   }
 
   @Override
   KotlinClassHeader createHeader() {
-    // TODO(b/70169921): may need to update if `rewrite` is implemented.
-    return metadata.getHeader();
+    KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
+    kmPackage.accept(writer);
+    return writer.write().getHeader();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index 0e195e7..1620eac 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName;
+import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType;
 import static kotlinx.metadata.FlagsKt.flagsOf;
 
 import com.android.tools.r8.graph.AppView;
@@ -20,7 +21,12 @@
 import kotlinx.metadata.KmType;
 import kotlinx.metadata.KmValueParameter;
 
-class KotlinMetadataSynthesizer {
+public class KotlinMetadataSynthesizer {
+
+  static boolean isExtension(KmFunction kmFunction) {
+    return kmFunction.getReceiverParameterType() != null;
+  }
+
   static KmType toKmType(String descriptor) {
     KmType kmType = new KmType(flagsOf());
     kmType.visitClass(descriptorToInternalName(descriptor));
@@ -61,34 +67,137 @@
     return kmType;
   }
 
-  static KmFunction toRenamedKmFunction(
-      DexMethod method, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
-    DexEncodedMethod encodedMethod = appView.definitionFor(method);
-    if (encodedMethod == null) {
-      return null;
+  private static boolean isCompatible(KmType kmType, DexType type, AppView<?> appView) {
+    if (kmType == null || type == null) {
+      return false;
     }
+    String descriptor = null;
+    if (appView.dexItemFactory().kotlin.knownTypeConversion.containsKey(type)) {
+      DexType convertedType = appView.dexItemFactory().kotlin.knownTypeConversion.get(type);
+      descriptor = convertedType.toDescriptorString();
+    }
+    if (descriptor == null) {
+      descriptor = type.toDescriptorString();
+    }
+    assert descriptor != null;
+    return descriptor.equals(getDescriptorFromKmType(kmType));
+  }
+
+  public static boolean isCompatibleFunction(
+      KmFunction function, DexEncodedMethod method, AppView<?> appView) {
+    if (!function.getName().equals(method.method.name.toString())) {
+      return false;
+    }
+    if (!isCompatible(function.getReturnType(), method.method.proto.returnType, appView)) {
+      return false;
+    }
+    List<KmValueParameter> parameters = function.getValueParameters();
+    if (method.method.proto.parameters.size() != parameters.size()) {
+      return false;
+    }
+    for (int i = 0; i < method.method.proto.parameters.size(); i++) {
+      KmType kmType = parameters.get(i).getType();
+      if (!isCompatible(kmType, method.method.proto.parameters.values[i], appView)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // TODO(b/70169921): Handling JVM extensions as well.
+  public static boolean isCompatibleExtension(
+      KmFunction extension, DexEncodedMethod method, AppView<?> appView) {
+    if (!extension.getName().equals(method.method.name.toString())) {
+      return false;
+    }
+    if (!isCompatible(extension.getReturnType(), method.method.proto.returnType, appView)) {
+      return false;
+    }
+    List<KmValueParameter> parameters = extension.getValueParameters();
+    if (method.method.proto.parameters.size() != parameters.size() + 1) {
+      return false;
+    }
+    assert method.method.proto.parameters.size() > 0;
+    assert extension.getReceiverParameterType() != null;
+    if (!isCompatible(
+        extension.getReceiverParameterType(), method.method.proto.parameters.values[0], appView)) {
+      return false;
+    }
+    for (int i = 1; i < method.method.proto.parameters.size(); i++) {
+      KmType kmType = parameters.get(i - 1).getType();
+      if (!isCompatible(kmType, method.method.proto.parameters.values[i], appView)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  static KmFunction toRenamedKmFunction(
+      DexEncodedMethod method,
+      KmFunction original,
+      AppView<AppInfoWithLiveness> appView,
+      NamingLens lens) {
+    return toRenamedKmFunctionHelper(method, original, appView, lens, false);
+  }
+
+  static KmFunction toRenamedKmFunctionAsExtension(
+      DexEncodedMethod method,
+      KmFunction original,
+      AppView<AppInfoWithLiveness> appView,
+      NamingLens lens) {
+    return toRenamedKmFunctionHelper(method, original, appView, lens, true);
+  }
+
+  private static KmFunction toRenamedKmFunctionHelper(
+      DexEncodedMethod method,
+      KmFunction original,
+      AppView<AppInfoWithLiveness> appView,
+      NamingLens lens,
+      boolean isExtension) {
     // For library overrides, synthesize @Metadata always.
     // For regular methods, make sure it is live.
-    if (!encodedMethod.isLibraryMethodOverride().isTrue()
-        && !appView.appInfo().liveMethods.contains(method)) {
+    if (!method.isLibraryMethodOverride().isTrue()
+        && !appView.appInfo().liveMethods.contains(method.method)) {
       return null;
     }
-    DexMethod renamedMethod = lens.lookupMethod(method, appView.dexItemFactory());
+    DexMethod renamedMethod = lens.lookupMethod(method.method, appView.dexItemFactory());
     // For a library method override, we should not have renamed it.
-    assert !encodedMethod.isLibraryMethodOverride().isTrue() || renamedMethod == method
+    assert !method.isLibraryMethodOverride().isTrue() || renamedMethod == method.method
         : method.toSourceString() + " -> " + renamedMethod.toSourceString();
-    // TODO(b/70169921): Consult kotlinx.metadata.Flag.Function for kind (e.g., suspend).
+    // TODO(b/70169921): {@link KmFunction.extensions} is private, i.e., no way to alter!
+    //   Thus, we rely on original metadata for now.
+    assert !isExtension || original != null;
     KmFunction kmFunction =
-        new KmFunction(encodedMethod.accessFlags.getAsKotlinFlags(), renamedMethod.name.toString());
-    KmType kmReturnType = toRenamedKmType(method.proto.returnType, appView, lens);
+        isExtension
+            ? original
+            // TODO(b/70169921): Consult kotlinx.metadata.Flag.Function for kind (e.g., suspend).
+            : new KmFunction(method.accessFlags.getAsKotlinFlags(), renamedMethod.name.toString());
+    KmType kmReturnType = toRenamedKmType(method.method.proto.returnType, appView, lens);
     assert kmReturnType != null;
     kmFunction.setReturnType(kmReturnType);
+    if (isExtension) {
+      assert method.method.proto.parameters.values.length > 0;
+      KmType kmReceiverType =
+          toRenamedKmType(method.method.proto.parameters.values[0], appView, lens);
+      assert kmReceiverType != null;
+      kmFunction.setReceiverParameterType(kmReceiverType);
+    }
     List<KmValueParameter> parameters = kmFunction.getValueParameters();
-    for (int i = 0; i < method.proto.parameters.values.length; i++) {
-      DexType paramType = method.proto.parameters.values[i];
-      DebugLocalInfo debugLocalInfo = encodedMethod.getParameterInfo().get(i);
-      String parameterName =
-          debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
+    parameters.clear();
+    populateKmValueParameters(parameters, method, appView, lens, isExtension);
+    return kmFunction;
+  }
+
+  private static void populateKmValueParameters(
+      List<KmValueParameter> parameters,
+      DexEncodedMethod method,
+      AppView<AppInfoWithLiveness> appView,
+      NamingLens lens,
+      boolean isExtension) {
+    for (int i = isExtension ? 1 : 0; i < method.method.proto.parameters.values.length; i++) {
+      DexType paramType = method.method.proto.parameters.values[i];
+      DebugLocalInfo debugLocalInfo = method.getParameterInfo().get(i);
+      String parameterName = debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
       // TODO(b/70169921): Consult kotlinx.metadata.Flag.ValueParameter.
       KmValueParameter kmValueParameter = new KmValueParameter(flagsOf(), parameterName);
       KmType kmParamType = toRenamedKmType(paramType, appView, lens);
@@ -96,6 +205,5 @@
       kmValueParameter.setType(kmParamType);
       parameters.add(kmValueParameter);
     }
-    return kmFunction;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index c3916cb..8222a43 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -16,6 +16,8 @@
 import java.io.File;
 import java.nio.file.Path;
 import java.util.Map;
+import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeVisitor;
 
 public class DescriptorUtils {
 
@@ -355,6 +357,36 @@
   }
 
   /**
+   * Get a fully qualified name from a classifier in Kotlin metadata.
+   * @param kmType where classifier contains Kotlin internal name, like "org/foo/bar/Baz.Nested"
+   * @return a class descriptor like "Lorg/foo/bar/Baz$Nested;"
+   */
+  public static String getDescriptorFromKmType(KmType kmType) {
+    if (kmType == null) {
+      return null;
+    }
+    Box<String> descriptor = new Box<>(null);
+    kmType.accept(new KmTypeVisitor() {
+      @Override
+      public void visitClass(String name) {
+        // TODO(b/70169921): Remove this if metadata lib is resilient to namespace relocation.
+        //  See b/70169921#comment25 for more details.
+        String backwardRelocatedName = name.replace("com/android/tools/r8/jetbrains/", "");
+        descriptor.set(getDescriptorFromKotlinClassifier(backwardRelocatedName));
+      }
+
+      @Override
+      public void visitTypeAlias(String name) {
+        // TODO(b/70169921): Remove this if metadata lib is resilient to namespace relocation.
+        //  See b/70169921#comment25 for more details.
+        String backwardRelocatedName = name.replace("com/android/tools/r8/jetbrains/", "");
+        descriptor.set(getDescriptorFromKotlinClassifier(backwardRelocatedName));
+      }
+    });
+    return descriptor.get();
+  }
+
+  /**
    * Get unqualified class name from its binary name.
    *
    * @param classBinaryName a class binary name i.e. "java/lang/Object" or "a/b/C$Inner"
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
index 358a016..e4ce45d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
@@ -3,11 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils.codeinspector;
 
+import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
+
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.DescriptorUtils;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -53,6 +54,7 @@
     return false;
   }
 
+  // TODO(b/145824437): This is a dup of DescriptorUtils#getDescriptorFromKmType
   private String getDescriptorFromKmType(KmType kmType) {
     if (kmType == null) {
       return null;
@@ -61,7 +63,12 @@
     kmType.accept(new KmTypeVisitor() {
       @Override
       public void visitClass(String name) {
-        descriptor.set(DescriptorUtils.getDescriptorFromKotlinClassifier(name));
+        descriptor.set(getDescriptorFromKotlinClassifier(name));
+      }
+
+      @Override
+      public void visitTypeAlias(String name) {
+        descriptor.set(getDescriptorFromKotlinClassifier(name));
       }
     });
     return descriptor.get();