Add support for inspecting Kotlin metadata.

Bug: 70169921, 145824437
Change-Id: I5a94c4e4783bb47a58614f229481ec759025ca69
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index 88c8181..c05b48f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DexValue.DexValueArray;
@@ -30,7 +31,7 @@
     DexAnnotation meta = clazz.annotations.getFirstMatching(kotlin.metadata.kotlinMetadataType);
     if (meta != null) {
       try {
-        return createKotlinInfo(kotlin, clazz, meta);
+        return createKotlinInfo(kotlin, clazz, meta.annotation);
       } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
         reporter.info(
             new StringDiagnostic("Class " + clazz.type.toSourceString()
@@ -44,12 +45,10 @@
     return null;
   }
 
-  private static KotlinInfo createKotlinInfo(
-      Kotlin kotlin,
-      DexClass clazz,
-      DexAnnotation meta) {
+  private static KotlinClassMetadata toKotlinClassMetadata(
+      Kotlin kotlin, DexEncodedAnnotation metadataAnnotation) {
     Map<DexString, DexAnnotationElement> elementMap = new IdentityHashMap<>();
-    for (DexAnnotationElement element : meta.annotation.elements) {
+    for (DexAnnotationElement element : metadataAnnotation.elements) {
       elementMap.put(element.name, element);
     }
 
@@ -74,7 +73,12 @@
     Integer xi = extraInt == null ? null : (Integer) extraInt.value.getBoxedValue();
 
     KotlinClassHeader header = new KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi);
-    KotlinClassMetadata kMetadata = KotlinClassMetadata.read(header);
+    return KotlinClassMetadata.read(header);
+  }
+
+  private static KotlinInfo createKotlinInfo(
+      Kotlin kotlin, DexClass clazz, DexEncodedAnnotation metadataAnnotation) {
+    KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, metadataAnnotation);
 
     if (kMetadata instanceof KotlinClassMetadata.Class) {
       return KotlinClass.fromKotlinClassMetadata(kMetadata, clazz);
@@ -87,7 +91,7 @@
     } else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
       return KotlinSyntheticClass.fromKotlinClassMetadata(kMetadata, kotlin, clazz);
     } else {
-      throw new MetadataError("unsupported 'k' value: " + k);
+      throw new MetadataError("unsupported 'k' value: " + kMetadata.getHeader().getKind());
     }
   }
 
@@ -129,5 +133,4 @@
       super(cause);
     }
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index 87f946c..a470bb3 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -4,8 +4,7 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -17,7 +16,9 @@
           + "java.lang.Object, java.lang.String)";
   protected static final String throwParameterIsNotNullExceptionSignature =
       "void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)";
-  protected static final String METADATA_DESCRIPTOR = "Lkotlin/Metadata;";
+  public static final String METADATA_DESCRIPTOR = "Lkotlin/Metadata;";
+  public static final String METADATA_TYPE =
+      DescriptorUtils.descriptorToJavaType(METADATA_DESCRIPTOR);
 
   private static final String RSRC = "kotlinR8TestResources";
 
@@ -48,13 +49,4 @@
   protected Path getMappingfile(String folder, String mappingFileName) {
     return Paths.get(ToolHelper.TESTS_DIR, RSRC, folder, mappingFileName);
   }
-
-  protected DexAnnotation retrieveMetadata(DexClass dexClass) {
-    for (DexAnnotation annotation : dexClass.annotations.annotations) {
-      if (annotation.annotation.type.toDescriptorString().equals(METADATA_DESCRIPTOR)) {
-        return annotation;
-      }
-    }
-    return null;
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index b561f2f..00d991a 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -219,6 +219,11 @@
     return KotlinCompilerTool.create(jdk, temp, kotlinCompiler, kotlinTargetVersion);
   }
 
+  public static KotlinCompilerTool kotlinc(
+      KotlinCompiler kotlinCompiler, KotlinTargetVersion kotlinTargetVersion) {
+    return kotlinc(TestRuntime.getCheckedInJdk9(), staticTemp, kotlinCompiler, kotlinTargetVersion);
+  }
+
   public KotlinCompilerTool kotlinc(
       CfRuntime jdk, KotlinCompiler kotlinCompiler, KotlinTargetVersion kotlinTargetVersion) {
     return KotlinCompilerTool.create(jdk, temp, kotlinCompiler, kotlinTargetVersion);
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index a3ec1a2..880eac4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -62,6 +62,11 @@
     return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values());
   }
 
+  // Some tests defined in subclasses, e.g., Metadata tests, don't care about access relaxation.
+  protected AbstractR8KotlinTestBase(KotlinTargetVersion kotlinTargetVersion) {
+    this(kotlinTargetVersion, false);
+  }
+
   protected AbstractR8KotlinTestBase(
       KotlinTargetVersion kotlinTargetVersion, boolean allowAccessModification) {
     super(kotlinTargetVersion);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index cdb5538..d866cca 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -3,11 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
 import com.android.tools.r8.utils.DescriptorUtils;
 
-abstract class KotlinMetadataTestBase extends KotlinTestBase {
+abstract class KotlinMetadataTestBase extends AbstractR8KotlinTestBase {
 
   KotlinMetadataTestBase(KotlinTargetVersion targetVersion) {
     super(targetVersion);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
index e256e37..640b18b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInExtensionTest.java
@@ -9,20 +9,20 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.List;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,15 +50,10 @@
   @BeforeClass
   public static void createLibJar() throws Exception {
     String extLibFolder = PKG_PREFIX + "/extension_lib";
-    extLibJar = getStaticTemp().newFile("ext_lib.jar").toPath();
-    ProcessResult processResult =
-        ToolHelper.runKotlinc(
-            null,
-            extLibJar,
-            null,
-            getKotlinFileInTest(extLibFolder, "B")
-        );
-    assertEquals(0, processResult.exitCode);
+    extLibJar =
+        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addSourceFiles(getKotlinFileInTest(extLibFolder, "B"))
+            .compile();
   }
 
   @Test
@@ -85,18 +80,19 @@
       assertThat(impl, isPresent());
       assertThat(impl, not(isRenamed()));
       // API entry is kept, hence the presence of Metadata.
-      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
-      assertNotNull(metadata);
-      assertThat(metadata.toString(), not(containsString("Super")));
+      KmClassSubject kmClass = impl.getKmClass();
+      assertThat(kmClass, isPresent());
+      List<ClassSubject> superTypes = kmClass.getSuperTypes();
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("Super")));
     });
 
-    Path r8ProcessedLibZip = temp.newFile("r8-lib.zip").toPath();
-    compileResult.writeToZip(r8ProcessedLibZip);
+    Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/extension_app";
     ProcessResult kotlinTestCompileResult =
         kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(r8ProcessedLibZip)
+            .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
             .setOutputPath(temp.newFolder().toPath())
             // TODO(b/143687784): update to just .compile() once fixed.
@@ -133,13 +129,16 @@
       assertThat(impl, isPresent());
       assertThat(impl, not(isRenamed()));
       // API entry is kept, hence the presence of Metadata.
-      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
-      assertNotNull(metadata);
-      assertThat(metadata.toString(), not(containsString("Super")));
+      KmClassSubject kmClass = impl.getKmClass();
+      assertThat(kmClass, isPresent());
+      List<ClassSubject> superTypes = kmClass.getSuperTypes();
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("Super")));
+      assertTrue(superTypes.stream().anyMatch(
+          supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
     });
 
-    Path libJar = temp.newFile("lib.jar").toPath();
-    compileResult.writeToZip(libJar);
+    Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/extension_app";
     Path output =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
similarity index 71%
rename from src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java
rename to src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
index 1030339..3fedef1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParametertypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInParameterTypeTest.java
@@ -9,27 +9,26 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.List;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class MetadataRenameInParametertypeTest extends KotlinMetadataTestBase {
+public class MetadataRenameInParameterTypeTest extends KotlinMetadataTestBase {
 
   private final TestParameters parameters;
 
@@ -39,33 +38,28 @@
         getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
   }
 
-  public MetadataRenameInParametertypeTest(
+  public MetadataRenameInParameterTypeTest(
       TestParameters parameters, KotlinTargetVersion targetVersion) {
     super(targetVersion);
     this.parameters = parameters;
   }
 
-  private static Path parameterLibJar;
+  private static Path parameterTypeLibJar;
 
   @BeforeClass
   public static void createLibJar() throws Exception {
-    String paramLibFolder = PKG_PREFIX + "/parametertype_lib";
-    parameterLibJar = getStaticTemp().newFile("param_lib.jar").toPath();
-    ProcessResult processResult =
-        ToolHelper.runKotlinc(
-            null,
-            parameterLibJar,
-            null,
-            getKotlinFileInTest(paramLibFolder, "lib")
-        );
-    assertEquals(0, processResult.exitCode);
+    String parameterTypeLibFolder = PKG_PREFIX + "/parametertype_lib";
+    parameterTypeLibJar =
+        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addSourceFiles(getKotlinFileInTest(parameterTypeLibFolder, "lib"))
+            .compile();
   }
 
   @Test
-  public void testMetadataInParameter_renamed() throws Exception {
+  public void testMetadataInParameterType_renamed() throws Exception {
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
-            .addProgramFiles(parameterLibJar)
+            .addProgramFiles(parameterTypeLibJar)
             // Keep non-private members of Impl
             .addKeepRules("-keep public class **.Impl { !private *; }")
             // Keep Itf, but allow minification.
@@ -84,14 +78,20 @@
       assertThat(impl, isPresent());
       assertThat(impl, not(isRenamed()));
       // API entry is kept, hence the presence of Metadata.
-      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
-      assertNotNull(metadata);
+      KmClassSubject kmClass = impl.getKmClass();
+      assertThat(kmClass, isPresent());
+      List<ClassSubject> superTypes = kmClass.getSuperTypes();
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("Itf")));
+      assertTrue(superTypes.stream().anyMatch(
+          supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
       // TODO(b/70169921): should not refer to Itf
-      assertThat(metadata.toString(), containsString("Itf"));
+      List<ClassSubject> parameterTypes = kmClass.getParameterTypesInFunctions();
+      assertTrue(parameterTypes.stream().anyMatch(
+          parameterType -> parameterType.getOriginalDescriptor().contains("Itf")));
     });
 
-    Path libJar = temp.newFile("lib.jar").toPath();
-    compileResult.writeToZip(libJar);
+    Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/parametertype_app";
     ProcessResult processResult =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
similarity index 71%
rename from src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
rename to src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
index fe815d0..2e92333 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInPropertyTypeTest.java
@@ -9,27 +9,26 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.List;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class MetadataRenameInPropertyTest extends KotlinMetadataTestBase {
+public class MetadataRenameInPropertyTypeTest extends KotlinMetadataTestBase {
 
   private final TestParameters parameters;
 
@@ -39,33 +38,28 @@
         getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
   }
 
-  public MetadataRenameInPropertyTest(
+  public MetadataRenameInPropertyTypeTest(
       TestParameters parameters, KotlinTargetVersion targetVersion) {
     super(targetVersion);
     this.parameters = parameters;
   }
 
-  private static Path propertyLibJar;
+  private static Path propertyTypeLibJar;
 
   @BeforeClass
   public static void createLibJar() throws Exception {
-    String propertyLibFolder = PKG_PREFIX + "/propertytype_lib";
-    propertyLibJar = getStaticTemp().newFile("property_lib.jar").toPath();
-    ProcessResult processResult =
-        ToolHelper.runKotlinc(
-            null,
-            propertyLibJar,
-            null,
-            getKotlinFileInTest(propertyLibFolder, "lib")
-        );
-    assertEquals(0, processResult.exitCode);
+    String propertyTypeLibFolder = PKG_PREFIX + "/propertytype_lib";
+    propertyTypeLibJar =
+        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib"))
+            .compile();
   }
 
   @Test
   public void testMetadataInProperty_renamed() throws Exception {
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
-            .addProgramFiles(propertyLibJar)
+            .addProgramFiles(propertyTypeLibJar)
             // Keep non-private members of Impl
             .addKeepRules("-keep public class **.Impl { !private *; }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
@@ -82,14 +76,20 @@
       assertThat(impl, isPresent());
       assertThat(impl, not(isRenamed()));
       // API entry is kept, hence the presence of Metadata.
-      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
-      assertNotNull(metadata);
+      KmClassSubject kmClass = impl.getKmClass();
+      assertThat(kmClass, isPresent());
+      List<ClassSubject> superTypes = kmClass.getSuperTypes();
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("Itf")));
+      assertTrue(superTypes.stream().anyMatch(
+          supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
       // TODO(b/70169921): should not refer to Itf
-      assertThat(metadata.toString(), containsString("Itf"));
+      List<ClassSubject> propertyReturnTypes = kmClass.getReturnTypesInProperties();
+      assertTrue(propertyReturnTypes.stream().anyMatch(
+          propertyType -> propertyType.getOriginalDescriptor().contains("Itf")));
     });
 
-    Path libJar = temp.newFile("lib.jar").toPath();
-    compileResult.writeToZip(libJar);
+    Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/propertytype_app";
     ProcessResult processResult =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
similarity index 72%
rename from src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java
rename to src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
index da6bf76..e7efcae 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturntypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInReturnTypeTest.java
@@ -9,27 +9,26 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.List;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class MetadataRenameInReturntypeTest extends KotlinMetadataTestBase {
+public class MetadataRenameInReturnTypeTest extends KotlinMetadataTestBase {
   private final TestParameters parameters;
 
   @Parameterized.Parameters(name = "{0} target: {1}")
@@ -38,33 +37,28 @@
         getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
   }
 
-  public MetadataRenameInReturntypeTest(
+  public MetadataRenameInReturnTypeTest(
       TestParameters parameters, KotlinTargetVersion targetVersion) {
     super(targetVersion);
     this.parameters = parameters;
   }
 
-  private static Path returntypeLibJar;
+  private static Path returnTypeLibJar;
 
   @BeforeClass
   public static void createLibJar() throws Exception {
-    String returntypeLibFolder = PKG_PREFIX + "/returntype_lib";
-    returntypeLibJar = getStaticTemp().newFile("returntype_lib.jar").toPath();
-    ProcessResult processResult =
-        ToolHelper.runKotlinc(
-            null,
-            returntypeLibJar,
-            null,
-            getKotlinFileInTest(returntypeLibFolder, "lib")
-        );
-    assertEquals(0, processResult.exitCode);
+    String returnTypeLibFolder = PKG_PREFIX + "/returntype_lib";
+    returnTypeLibJar =
+        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addSourceFiles(getKotlinFileInTest(returnTypeLibFolder, "lib"))
+            .compile();
   }
 
   @Test
-  public void testmetadataInReturnType_renamed() throws Exception {
+  public void testMetadataInReturnType_renamed() throws Exception {
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
-            .addProgramFiles(returntypeLibJar)
+            .addProgramFiles(returnTypeLibJar)
             // Keep non-private members of Impl
             .addKeepRules("-keep public class **.Impl { !private *; }")
             // Keep Itf, but allow minification.
@@ -83,14 +77,20 @@
       assertThat(impl, isPresent());
       assertThat(impl, not(isRenamed()));
       // API entry is kept, hence the presence of Metadata.
-      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
-      assertNotNull(metadata);
+      KmClassSubject kmClass = impl.getKmClass();
+      assertThat(kmClass, isPresent());
+      List<ClassSubject> superTypes = kmClass.getSuperTypes();
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("Itf")));
+      assertTrue(superTypes.stream().anyMatch(
+          supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
       // TODO(b/70169921): should not refer to Itf
-      assertThat(metadata.toString(), containsString("Itf"));
+      List<ClassSubject> functionReturnTypes = kmClass.getReturnTypesInFunctions();
+      assertTrue(functionReturnTypes.stream().anyMatch(
+          returnType -> returnType.getOriginalDescriptor().contains("Itf")));
     });
 
-    Path libJar = temp.newFile("lib.jar").toPath();
-    compileResult.writeToZip(libJar);
+    Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/returntype_app";
     ProcessResult processResult =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java
similarity index 72%
rename from src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java
rename to src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java
index dc29f2c..4ad8e97 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSupertypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSuperTypeTest.java
@@ -6,29 +6,27 @@
 import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.not;
 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.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.List;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class MetadataRenameInSupertypeTest extends KotlinMetadataTestBase {
+public class MetadataRenameInSuperTypeTest extends KotlinMetadataTestBase {
 
   private final TestParameters parameters;
 
@@ -38,34 +36,30 @@
         getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
   }
 
-  public MetadataRenameInSupertypeTest(
+  public MetadataRenameInSuperTypeTest(
       TestParameters parameters, KotlinTargetVersion targetVersion) {
     super(targetVersion);
     this.parameters = parameters;
   }
 
-  private static Path supertypeLibJar;
+  private static Path superTypeLibJar;
 
   @BeforeClass
   public static void createLibJar() throws Exception {
-    String supertypeLibFolder = PKG_PREFIX + "/supertype_lib";
-    supertypeLibJar = getStaticTemp().newFile("supertype_lib.jar").toPath();
-    ProcessResult processResult =
-        ToolHelper.runKotlinc(
-            null,
-            supertypeLibJar,
-            null,
-            getKotlinFileInTest(supertypeLibFolder, "impl"),
-            getKotlinFileInTest(supertypeLibFolder + "/internal", "itf")
-        );
-    assertEquals(0, processResult.exitCode);
+    String superTypeLibFolder = PKG_PREFIX + "/supertype_lib";
+    superTypeLibJar =
+        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addSourceFiles(
+                getKotlinFileInTest(superTypeLibFolder, "impl"),
+                getKotlinFileInTest(superTypeLibFolder + "/internal", "itf"))
+            .compile();
   }
 
   @Test
   public void b143687784_merged() throws Exception {
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
-            .addProgramFiles(supertypeLibJar)
+            .addProgramFiles(superTypeLibJar)
             // Keep non-private members except for ones in `internal` definitions.
             .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
@@ -81,25 +75,27 @@
       assertThat(impl, isPresent());
       assertThat(impl, not(isRenamed()));
       // API entry is kept, hence the presence of Metadata.
-      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
-      assertNotNull(metadata);
-      assertThat(metadata.toString(), not(containsString("internal")));
-      assertThat(metadata.toString(), not(containsString("Itf")));
+      KmClassSubject kmClass = impl.getKmClass();
+      assertThat(kmClass, isPresent());
+      List<ClassSubject> superTypes = kmClass.getSuperTypes();
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("internal")));
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("Itf")));
     });
 
-    Path r8ProcessedLibZip = temp.newFile("r8-lib.zip").toPath();
-    compileResult.writeToZip(r8ProcessedLibZip);
+    Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/supertype_app";
     Path output =
         kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8)
-            .addClasspathFiles(r8ProcessedLibZip)
+            .addClasspathFiles(libJar)
             .addSourceFiles(getKotlinFileInTest(appFolder, "main"))
             .setOutputPath(temp.newFolder().toPath())
             .compile();
 
     testForJvm()
-        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), r8ProcessedLibZip)
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
         .addClasspath(output)
         .run(parameters.getRuntime(), pkg + ".supertype_app.MainKt")
         .assertSuccessWithOutputLines("Impl::foo", "Program::foo");
@@ -109,7 +105,7 @@
   public void b143687784_renamed() throws Exception {
     R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
-            .addProgramFiles(supertypeLibJar)
+            .addProgramFiles(superTypeLibJar)
             // Keep non-private members except for ones in `internal` definitions.
             .addKeepRules("-keep public class !**.internal.**, * { !private *; }")
             // Keep `internal` definitions, but allow minification.
@@ -128,15 +124,18 @@
       assertThat(impl, isPresent());
       assertThat(impl, not(isRenamed()));
       // API entry is kept, hence the presence of Metadata.
-      DexAnnotation metadata = retrieveMetadata(impl.getDexClass());
-      assertNotNull(metadata);
-      assertThat(metadata.toString(), not(containsString("internal")));
-      assertThat(metadata.toString(), not(containsString("Itf")));
-      assertThat(metadata.toString(), containsString("a/a"));
+      KmClassSubject kmClass = impl.getKmClass();
+      assertThat(kmClass, isPresent());
+      List<ClassSubject> superTypes = kmClass.getSuperTypes();
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("internal")));
+      assertTrue(superTypes.stream().noneMatch(
+          supertype -> supertype.getFinalDescriptor().contains("Itf")));
+      assertTrue(superTypes.stream().anyMatch(
+          supertype -> supertype.getFinalDescriptor().equals(itf.getFinalDescriptor())));
     });
 
-    Path libJar = temp.newFile("lib.jar").toPath();
-    compileResult.writeToZip(libJar);
+    Path libJar = compileResult.writeToZip();
 
     String appFolder = PKG_PREFIX + "/supertype_app";
     Path output =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 00b5606..35dd597 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -7,8 +7,6 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.R8TestRunResult;
@@ -16,6 +14,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.util.Collection;
@@ -61,11 +60,12 @@
     assertThat(clazz, isPresent());
     assertThat(clazz, not(isRenamed()));
     // Main class is kept, hence the presence of Metadata.
-    assertNotNull(retrieveMetadata(clazz.getDexClass()));
+    AnnotationSubject annotationSubject = clazz.annotation(METADATA_TYPE);
+    assertThat(annotationSubject, isPresent());
     ClassSubject impl1 = inspector.clazz(implementer1ClassName);
     assertThat(impl1, isPresent());
     assertThat(impl1, isRenamed());
     // All other classes can be renamed, hence the absence of Metadata;
-    assertNull(retrieveMetadata(impl1.getDexClass()));
+    assertThat(impl1.annotation(METADATA_TYPE), not(isPresent()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_lib/B.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_lib/B.kt
index a6fe9ab..df51d42 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_lib/B.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_lib/B.kt
@@ -13,7 +13,7 @@
   }
 }
 
-class B : Super() { }
+class B : Super()
 
 fun B.extension() {
   doStuff()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/internal/itf.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/internal/itf.kt
index 50e18d4..7fff074 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/internal/itf.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/supertype_lib/internal/itf.kt
@@ -4,5 +4,5 @@
 package com.android.tools.r8.kotlin.metadata.supertype_lib.internal
 
 interface Itf {
-  fun foo();
+  fun foo()
 }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesTest.java b/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesTest.java
index f62e21b..f618e93 100644
--- a/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesTest.java
@@ -97,7 +97,7 @@
           localVariableTable.get(0),
           0,
           "this",
-          classSubject.asFoundClassSubject().asTypeSybject(),
+          classSubject.asFoundClassSubject().asTypeSubject(),
           null);
       checkLocalVariable(
           localVariableTable.get(1), 1, "parameter1", inspector.getTypeSubject("int"), null);
@@ -121,7 +121,7 @@
           localVariableTable.get(0),
           0,
           "this",
-          classSubject.asFoundClassSubject().asTypeSybject(),
+          classSubject.asFoundClassSubject().asTypeSubject(),
           null);
       checkLocalVariable(
           localVariableTable.get(1), 1, "parameter1", inspector.getTypeSubject("long"), null);
@@ -141,7 +141,7 @@
           localVariableTable.get(0),
           0,
           "this",
-          classSubject.asFoundClassSubject().asTypeSybject(),
+          classSubject.asFoundClassSubject().asTypeSubject(),
           null);
       checkLocalVariable(
           localVariableTable.get(1),
diff --git a/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesUnsortedLocalVariablesTableTest.java b/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesUnsortedLocalVariablesTableTest.java
index d858322..c92d7e7 100644
--- a/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesUnsortedLocalVariablesTableTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keepparameternames/KeepParameterNamesUnsortedLocalVariablesTableTest.java
@@ -73,7 +73,7 @@
           localVariableTable.get(0),
           0,
           "this",
-          classSubject.asFoundClassSubject().asTypeSybject(),
+          classSubject.asFoundClassSubject().asTypeSubject(),
           null);
       checkLocalVariable(
           localVariableTable.get(1), 1, "parameter1", inspector.getTypeSubject("int"), null);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 8e2f554..e665dcf 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -137,4 +137,9 @@
   public String getFinalSignatureAttribute() {
     return null;
   }
+
+  @Override
+  public KmClassSubject getKmClass() {
+    return null;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
new file mode 100644
index 0000000..201f644
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmClassSubject.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2019, 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.utils.codeinspector;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.kotlin.Kotlin;
+import kotlinx.metadata.KmClass;
+
+public class AbsentKmClassSubject extends KmClassSubject {
+
+  @Override
+  public DexClass getDexClass() {
+    return null;
+  }
+
+  @Override
+  public KmClass getKmClass(Kotlin kotlin) {
+    return null;
+  }
+
+  @Override
+  public boolean isPresent() {
+    return false;
+  }
+
+  @Override
+  public boolean isRenamed() {
+    return false;
+  }
+
+  @Override
+  public boolean isSynthetic() {
+    return false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 8b8f519..705337c 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -166,4 +166,6 @@
   public abstract String getOriginalSignatureAttribute();
 
   public abstract String getFinalSignatureAttribute();
+
+  public abstract KmClassSubject getKmClass();
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
index 0a53c91..a97f040 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
@@ -12,7 +12,7 @@
 
   private final DexAnnotation annotation;
 
-  public FoundAnnotationSubject(DexAnnotation annotation) {
+  FoundAnnotationSubject(DexAnnotation annotation) {
     this.annotation = annotation;
   }
 
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 45276f4..adc0dbd 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
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import static com.android.tools.r8.KotlinTestBase.METADATA_TYPE;
+import static org.junit.Assert.assertTrue;
+
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -23,6 +26,7 @@
 import com.android.tools.r8.utils.StringUtils;
 import java.util.List;
 import java.util.function.Consumer;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
 
 public class FoundClassSubject extends ClassSubject {
 
@@ -324,7 +328,21 @@
     return dexClass.toSourceString();
   }
 
-  public TypeSubject asTypeSybject() {
+  public TypeSubject asTypeSubject() {
     return new TypeSubject(codeInspector, getDexClass().type);
   }
+
+  @Override
+  public KmClassSubject getKmClass() {
+    AnnotationSubject annotationSubject = annotation(METADATA_TYPE);
+    if (!annotationSubject.isPresent()) {
+      return new AbsentKmClassSubject();
+    }
+    KotlinClassMetadata metadata =
+        KotlinClassMetadataReader.toKotlinClassMetadata(
+            codeInspector.getFactory().kotlin, annotationSubject.getAnnotation());
+    assertTrue(metadata instanceof KotlinClassMetadata.Class);
+    KotlinClassMetadata.Class kClass = (KotlinClassMetadata.Class) metadata;
+    return new FoundKmClassSubject(codeInspector, getDexClass(), kClass.toKmClass());
+  }
 }
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
new file mode 100644
index 0000000..358a016
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
@@ -0,0 +1,145 @@
+// Copyright (c) 2019, 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.utils.codeinspector;
+
+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;
+import kotlinx.metadata.KmClass;
+import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeVisitor;
+
+public class FoundKmClassSubject extends KmClassSubject {
+  private final CodeInspector codeInspector;
+  private final DexClass clazz;
+  private final KmClass kmClass;
+
+  FoundKmClassSubject(CodeInspector codeInspector, DexClass clazz, KmClass kmClass) {
+    this.codeInspector = codeInspector;
+    this.clazz = clazz;
+    this.kmClass = kmClass;
+  }
+
+  @Override
+  public DexClass getDexClass() {
+    return clazz;
+  }
+
+  @Override
+  public KmClass getKmClass(Kotlin kotlin) {
+    return kmClass;
+  }
+
+  @Override
+  public boolean isPresent() {
+    return true;
+  }
+
+  @Override
+  public boolean isRenamed() {
+    return !clazz.type.getInternalName().equals(kmClass.name);
+  }
+
+  @Override
+  public boolean isSynthetic() {
+    // TODO(b/70169921): This should return `true` conditionally if we start synthesizing @Metadata
+    //   from scratch.
+    return false;
+  }
+
+  private 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) {
+        descriptor.set(DescriptorUtils.getDescriptorFromKotlinClassifier(name));
+      }
+    });
+    return descriptor.get();
+  }
+
+  @Override
+  public List<String> getSuperTypeDescriptors() {
+    return kmClass.getSupertypes().stream()
+        .map(this::getDescriptorFromKmType)
+        .filter(Objects::nonNull)
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public List<String> getParameterTypeDescriptorsInFunctions() {
+    return kmClass.getFunctions().stream()
+        .flatMap(kmFunction ->
+            kmFunction.getValueParameters().stream()
+                .map(kmValueParameter -> getDescriptorFromKmType(kmValueParameter.getType()))
+                .filter(Objects::nonNull))
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public List<String> getReturnTypeDescriptorsInFunctions() {
+    return kmClass.getFunctions().stream()
+        .map(kmFunction -> getDescriptorFromKmType(kmFunction.getReturnType()))
+        .filter(Objects::nonNull)
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public List<String> getReturnTypeDescriptorsInProperties() {
+    return kmClass.getProperties().stream()
+        .map(kmProperty -> getDescriptorFromKmType(kmProperty.getReturnType()))
+        .filter(Objects::nonNull)
+        .collect(Collectors.toList());
+  }
+
+  private ClassSubject getClassSubjectFromKmType(KmType kmType) {
+    String descriptor = getDescriptorFromKmType(kmType);
+    if (descriptor == null) {
+      return new AbsentClassSubject();
+    }
+    return codeInspector.clazz(Reference.classFromDescriptor(descriptor));
+  }
+
+  @Override
+  public List<ClassSubject> getSuperTypes() {
+    return kmClass.getSupertypes().stream()
+        .map(this::getClassSubjectFromKmType)
+        .filter(ClassSubject::isPresent)
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public List<ClassSubject> getParameterTypesInFunctions() {
+    return kmClass.getFunctions().stream()
+        .flatMap(kmFunction ->
+            kmFunction.getValueParameters().stream()
+                .map(kmValueParameter -> getClassSubjectFromKmType(kmValueParameter.getType()))
+                .filter(ClassSubject::isPresent))
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public List<ClassSubject> getReturnTypesInFunctions() {
+    return kmClass.getFunctions().stream()
+        .map(kmFunction -> getClassSubjectFromKmType(kmFunction.getReturnType()))
+        .filter(ClassSubject::isPresent)
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public List<ClassSubject> getReturnTypesInProperties() {
+    return kmClass.getProperties().stream()
+        .map(kmProperty -> getClassSubjectFromKmType(kmProperty.getReturnType()))
+        .filter(ClassSubject::isPresent)
+        .collect(Collectors.toList());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
new file mode 100644
index 0000000..aac5196
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmClassSubject.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2019, 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.utils.codeinspector;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.kotlin.Kotlin;
+import java.util.List;
+import kotlinx.metadata.KmClass;
+
+public abstract class KmClassSubject extends Subject {
+  public abstract DexClass getDexClass();
+  public abstract KmClass getKmClass(Kotlin kotlin);
+
+  public List<String> getSuperTypeDescriptors() {
+    return null;
+  }
+
+  public List<String> getParameterTypeDescriptorsInFunctions() {
+    return null;
+  }
+
+  public List<String> getReturnTypeDescriptorsInFunctions() {
+    return null;
+  }
+
+  public List<String> getReturnTypeDescriptorsInProperties() {
+    return null;
+  }
+
+  public List<ClassSubject> getSuperTypes() {
+    return null;
+  }
+
+  public List<ClassSubject> getParameterTypesInFunctions() {
+    return null;
+  }
+
+  public List<ClassSubject> getReturnTypesInFunctions() {
+    return null;
+  }
+
+  public List<ClassSubject> getReturnTypesInProperties() {
+    return null;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KotlinClassMetadataReader.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KotlinClassMetadataReader.java
new file mode 100644
index 0000000..b3a0143
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KotlinClassMetadataReader.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2019, 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.utils.codeinspector;
+
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.kotlin.Kotlin;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+// TODO(b/145824437): This is a dup of the same class in source to avoid the error while building
+//  keep rules for r8lib. Should be able to avoid this redundancy at build configuration level or
+//  change -printusage to apply mappings regarding relocated deps.
+class KotlinClassMetadataReader {
+  static KotlinClassMetadata toKotlinClassMetadata(
+      Kotlin kotlin, DexEncodedAnnotation metadataAnnotation) {
+    Map<DexString, DexAnnotationElement> elementMap = new IdentityHashMap<>();
+    for (DexAnnotationElement element : metadataAnnotation.elements) {
+      elementMap.put(element.name, element);
+    }
+
+    DexAnnotationElement kind = elementMap.get(kotlin.metadata.kind);
+    if (kind == null) {
+      throw new MetadataError("element 'k' is missing.");
+    }
+    Integer k = (Integer) kind.value.getBoxedValue();
+    DexAnnotationElement metadataVersion = elementMap.get(kotlin.metadata.metadataVersion);
+    int[] mv = metadataVersion == null ? null : getUnboxedIntArray(metadataVersion.value, "mv");
+    DexAnnotationElement bytecodeVersion = elementMap.get(kotlin.metadata.bytecodeVersion);
+    int[] bv = bytecodeVersion == null ? null : getUnboxedIntArray(bytecodeVersion.value, "bv");
+    DexAnnotationElement data1 = elementMap.get(kotlin.metadata.data1);
+    String[] d1 = data1 == null ? null : getUnboxedStringArray(data1.value, "d1");
+    DexAnnotationElement data2 = elementMap.get(kotlin.metadata.data2);
+    String[] d2 = data2 == null ? null : getUnboxedStringArray(data2.value, "d2");
+    DexAnnotationElement extraString = elementMap.get(kotlin.metadata.extraString);
+    String xs = extraString == null ? null : getUnboxedString(extraString.value, "xs");
+    DexAnnotationElement packageName = elementMap.get(kotlin.metadata.packageName);
+    String pn = packageName == null ? null : getUnboxedString(packageName.value, "pn");
+    DexAnnotationElement extraInt = elementMap.get(kotlin.metadata.extraInt);
+    Integer xi = extraInt == null ? null : (Integer) extraInt.value.getBoxedValue();
+
+    KotlinClassHeader header = new KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi);
+    return KotlinClassMetadata.read(header);
+  }
+
+  private static int[] getUnboxedIntArray(DexValue v, String elementName) {
+    if (!(v instanceof DexValueArray)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    DexValueArray intArrayValue = (DexValueArray) v;
+    DexValue[] values = intArrayValue.getValues();
+    int[] result = new int [values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = (Integer) values[i].getBoxedValue();
+    }
+    return result;
+  }
+
+  private static String[] getUnboxedStringArray(DexValue v, String elementName) {
+    if (!(v instanceof DexValueArray)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    DexValueArray stringArrayValue = (DexValueArray) v;
+    DexValue[] values = stringArrayValue.getValues();
+    String[] result = new String [values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = getUnboxedString(values[i], elementName + "[" + i + "]");
+    }
+    return result;
+  }
+
+  private static String getUnboxedString(DexValue v, String elementName) {
+    if (!(v instanceof DexValueString)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    return ((DexValueString) v).getValue().toString();
+  }
+
+  private static class MetadataError extends RuntimeException {
+    MetadataError(String cause) {
+      super(cause);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 1fd4abe..d9f11d6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -57,6 +57,10 @@
       type = "method";
     } else if (subject instanceof FieldSubject) {
       type = "field";
+    } else if (subject instanceof AnnotationSubject) {
+      type = "annotation";
+    } else if (subject instanceof KmClassSubject) {
+      type = "@Metadata.KmClass";
     }
     return type;
   }
@@ -69,6 +73,10 @@
       name = ((MethodSubject) subject).getOriginalName();
     } else if (subject instanceof FieldSubject) {
       name = ((FieldSubject) subject).getOriginalName();
+    } else if (subject instanceof AnnotationSubject) {
+      name = ((AnnotationSubject) subject).getAnnotation().type.toSourceString();
+    } else if (subject instanceof KmClassSubject) {
+      name = ((KmClassSubject) subject).getDexClass().toSourceString();
     }
     return name;
   }