Revert "Reland "[Metadata] Update kotlin-metadata-jvm to version 0.6.0""

This reverts commit 04d43e37773657c2def9e5a2b6412fbbaaa6d133.


Revert "[Metadata] Update service import in sanity check test"

This reverts commit dde8cf4cd2e452225400d77819767cd48406cd41.


Revert "[Metadata] Move away from deprecated metadata visitors"

This reverts commit 4ff176f892492b8bbb1a647f83cc0a9e679b40d9.


Revert "[Metadata] Remove instantiation of KotlinClassHeader"

This reverts commit 90c436ad113535b41a133424d817a7dcda1149a4.

Bug: b/266682027
Change-Id: If015de4e8ae0068ab15fb1e88e53aa1f37559bcb
diff --git a/build.gradle b/build.gradle
index 462e425..abf8ddf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -44,8 +44,8 @@
     mockitoVersion = '2.10.0'
     // The kotlin version is only here to specify the kotlin language level,
     // all kotlin compilations are done in tests.
-    kotlinVersion = '1.8.0'
-    kotlinExtMetadataJVMVersion = '0.6.0'
+    kotlinVersion = '1.6.0'
+    kotlinExtMetadataJVMVersion = '0.5.0'
     smaliVersion = '2.2b4'
     errorproneVersion = '2.3.2'
     testngVersion = '6.10'
@@ -285,7 +285,7 @@
     main17Implementation group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
     main17Implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
     main17Implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
-
+    
     examplesTestNGRunnerCompile group: 'org.testng', name: 'testng', version: testngVersion
 
     testCompile sourceSets.examples.output
diff --git a/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java b/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java
new file mode 100644
index 0000000..65bdeec
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java
@@ -0,0 +1,134 @@
+// 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.kotlin;
+
+import kotlinx.metadata.KmAnnotation;
+import kotlinx.metadata.KmContractVisitor;
+import kotlinx.metadata.KmEffectExpressionVisitor;
+import kotlinx.metadata.KmEffectInvocationKind;
+import kotlinx.metadata.KmEffectType;
+import kotlinx.metadata.KmEffectVisitor;
+import kotlinx.metadata.KmFunctionVisitor;
+import kotlinx.metadata.KmLambdaVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
+import kotlinx.metadata.KmTypeAliasVisitor;
+import kotlinx.metadata.KmTypeParameterVisitor;
+import kotlinx.metadata.KmTypeVisitor;
+import kotlinx.metadata.KmValueParameterVisitor;
+import kotlinx.metadata.KmVariance;
+import kotlinx.metadata.KmVersionRequirementVisitor;
+
+/**
+ * The reason for having these visitor providers is to make the separation of concern a bit easier
+ * while also working with the kotlinx.metadata visitors as shown by the following example:
+ *
+ * <p>Say we have the following structure KotlinTypeInfo: { TypeProjects:
+ * [KotlinTypeProjectionInfo(StarProjection)] }
+ *
+ * <p>Now the KmTypeVisitor (we use to generate the KotlinTypeInfo, has a visitProjection(int flags,
+ * KmVariance variance) generator that will return a new KmTypeVisitor, however, if the projection
+ * is a star projection, the generator visitStarProjection() should be used.
+ *
+ * <p>The information about the projection being a star projection is contained in the
+ * KotlinTypeProjectionInfo. As a result, KotlinTypeInfo should query the object for picking the
+ * right generator, the KotlinTypeProjectionInfo should return a KmTypeProjection object, or we
+ * simply capture the generators lazily (by these providers), such that the object with all the
+ * information can decide when/what object to create.
+ *
+ * <p>Another benefit of this approach than using the build in visitors is that shared structures,
+ * such as KotlinAnnotationInfo that can be on type-aliases, functions and properties will not have
+ * to take in three different type of visitors.
+ */
+public class KmVisitorProviders {
+
+  @FunctionalInterface
+  public interface KmAnnotationVisitorProvider {
+
+    void get(KmAnnotation annotation);
+  }
+
+  @FunctionalInterface
+  public interface KmFunctionVisitorProvider {
+
+    KmFunctionVisitor get(int flags, String name);
+  }
+
+  public interface KmLambdaVisitorProvider {
+
+    KmLambdaVisitor get();
+  }
+
+  @FunctionalInterface
+  public interface KmPropertyVisitorProvider {
+
+    KmPropertyVisitor get(int flags, String name, int getterFlags, int setterFlags);
+  }
+
+  @FunctionalInterface
+  public interface KmTypeAliasVisitorProvider {
+
+    KmTypeAliasVisitor get(int flags, String name);
+  }
+
+  @FunctionalInterface
+  public interface KmTypeParameterVisitorProvider {
+
+    KmTypeParameterVisitor get(int flags, String name, int id, KmVariance variance);
+  }
+
+  @FunctionalInterface
+  public interface KmTypeProjectionVisitorProvider {
+
+    KmTypeVisitor get(int flags, KmVariance variance);
+  }
+
+  @FunctionalInterface
+  public interface KmTypeStarProjectionVisitorProvider {
+
+    void get();
+  }
+
+  @FunctionalInterface
+  public interface KmTypeVisitorProvider {
+
+    KmTypeVisitor get(int flags);
+  }
+
+  @FunctionalInterface
+  public interface KmValueParameterVisitorProvider {
+
+    KmValueParameterVisitor get(int flags, String name);
+  }
+
+  @FunctionalInterface
+  public interface KmFlexibleUpperBoundVisitorProvider {
+
+    KmTypeVisitor get(int flags, String typeFlexibilityId);
+  }
+
+  @FunctionalInterface
+  public interface KmVersionRequirementVisitorProvider {
+
+    KmVersionRequirementVisitor get();
+  }
+
+  @FunctionalInterface
+  public interface KmContractVisitorProvider {
+
+    KmContractVisitor get();
+  }
+
+  @FunctionalInterface
+  public interface KmEffectVisitorProvider {
+
+    KmEffectVisitor get(KmEffectType type, KmEffectInvocationKind effectInvocationKind);
+  }
+
+  @FunctionalInterface
+  public interface KmEffectExpressionVisitorProvider {
+
+    KmEffectExpressionVisitor get();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
index 759ef69..249f68f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
@@ -14,7 +14,6 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmAnnotation;
 import kotlinx.metadata.KmAnnotationArgument;
 
@@ -50,7 +49,8 @@
     return builder.build();
   }
 
-  boolean rewrite(Consumer<KmAnnotation> annotationConsumer, AppView<?> appView) {
+  boolean rewrite(
+      KmVisitorProviders.KmAnnotationVisitorProvider visitorProvider, AppView<?> appView) {
     BooleanBox rewritten = new BooleanBox(false);
     rewritten.or(
         annotationType.toRenamedDescriptorOrDefault(
@@ -72,7 +72,7 @@
                                 }
                               },
                               appView)));
-              annotationConsumer.accept(new KmAnnotation(classifier, rewrittenArguments));
+              visitorProvider.get(new KmAnnotation(classifier, rewrittenArguments));
             },
             appView,
             null));
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index d59a9ca..da35a69 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -4,12 +4,9 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmFieldSignature;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
-import static kotlinx.metadata.jvm.KotlinClassMetadata.Companion;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -30,12 +27,13 @@
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
-import kotlin.Metadata;
 import kotlinx.metadata.KmClass;
 import kotlinx.metadata.KmConstructor;
 import kotlinx.metadata.KmType;
+import kotlinx.metadata.jvm.JvmClassExtensionVisitor;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
 import kotlinx.metadata.jvm.JvmMethodSignature;
+import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 
 public class KotlinClassInfo implements KotlinClassLevelInfo {
@@ -289,7 +287,7 @@
   }
 
   @Override
-  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
     KmClass kmClass = new KmClass();
     // TODO(b/154348683): Set flags.
     kmClass.setFlags(flags);
@@ -366,29 +364,27 @@
     // Rewrite functions, type-aliases and type-parameters.
     rewritten |=
         declarationContainerInfo.rewrite(
-            kmClass.getFunctions()::add,
-            kmClass.getProperties()::add,
-            kmClass.getTypeAliases()::add,
+            kmClass::visitFunction,
+            kmClass::visitProperty,
+            kmClass::visitTypeAlias,
             clazz,
             appView,
             rewrittenReferences);
     // Rewrite type parameters.
-    rewritten |=
-        rewriteList(
-            appView, typeParameters, kmClass.getTypeParameters(), KotlinTypeParameterInfo::rewrite);
+    for (KotlinTypeParameterInfo typeParameter : typeParameters) {
+      rewritten |= typeParameter.rewrite(kmClass::visitTypeParameter, appView);
+    }
     // Rewrite super types.
-    List<KmType> rewrittenSuperTypes = kmClass.getSupertypes();
     for (KotlinTypeInfo superType : superTypes) {
       // Ensure the rewritten super type is not this type.
       if (clazz.getType() != superType.rewriteType(appView.graphLens())) {
-        rewritten |= superType.rewrite(rewrittenSuperTypes::add, appView);
+        rewritten |= superType.rewrite(kmClass::visitSupertype, appView);
       } else {
         rewritten = true;
       }
     }
     // Rewrite nested classes.
-    List<String> rewrittenNestedClasses = kmClass.getNestedClasses();
-    for (KotlinTypeReference nestedClass : this.nestedClasses) {
+    for (KotlinTypeReference nestedClass : nestedClasses) {
       Box<String> nestedDescriptorBox = new Box<>();
       boolean nestedClassRewritten =
           nestedClass.toRenamedBinaryNameOrDefault(nestedDescriptorBox::set, appView, null);
@@ -398,21 +394,20 @@
           // is the name we should record.
           String nestedDescriptor = nestedDescriptorBox.get();
           int innerClassIndex = nestedDescriptor.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
-          rewrittenNestedClasses.add(nestedDescriptor.substring(innerClassIndex + 1));
+          kmClass.visitNestedClass(nestedDescriptor.substring(innerClassIndex + 1));
         } else {
-          rewrittenNestedClasses.add(nestedClass.getOriginalName());
+          kmClass.visitNestedClass(nestedClass.getOriginalName());
         }
       }
       rewritten |= nestedClassRewritten;
     }
-    // Rewrite sealed sub-classes.
-    List<String> rewrittenSealedClasses = kmClass.getSealedSubclasses();
+    // Rewrite sealed sub classes.
     for (KotlinTypeReference sealedSubClass : sealedSubClasses) {
       rewritten |=
           sealedSubClass.toRenamedBinaryNameOrDefault(
               sealedName -> {
                 if (sealedName != null) {
-                  rewrittenSealedClasses.add(
+                  kmClass.visitSealedSubclass(
                       sealedName.replace(
                           DescriptorUtils.INNER_CLASS_SEPARATOR,
                           DescriptorUtils.JAVA_PACKAGE_SEPARATOR));
@@ -421,36 +416,37 @@
               appView,
               null);
     }
-    rewritten |= versionRequirements.rewrite(kmClass.getVersionRequirements()::addAll);
+    rewritten |= versionRequirements.rewrite(kmClass::visitVersionRequirement);
     if (inlineClassUnderlyingPropertyName != null && inlineClassUnderlyingType != null) {
       kmClass.setInlineClassUnderlyingPropertyName(inlineClassUnderlyingPropertyName);
       rewritten |=
-          inlineClassUnderlyingType.rewrite(kmClass::setInlineClassUnderlyingType, appView);
+          inlineClassUnderlyingType.rewrite(kmClass::visitInlineClassUnderlyingType, appView);
     }
-    rewritten |=
-        rewriteList(
-            appView,
-            contextReceiverTypes,
-            kmClass.getContextReceiverTypes(),
-            KotlinTypeInfo::rewrite);
-    JvmExtensionsKt.setJvmFlags(kmClass, jvmFlags);
-    JvmExtensionsKt.setModuleName(kmClass, moduleName);
+    for (KotlinTypeInfo contextReceiverType : contextReceiverTypes) {
+      rewritten |= contextReceiverType.rewrite(kmClass::visitContextReceiverType, appView);
+    }
+    JvmClassExtensionVisitor extensionVisitor =
+        (JvmClassExtensionVisitor) kmClass.visitExtensions(JvmClassExtensionVisitor.TYPE);
+    extensionVisitor.visitJvmFlags(jvmFlags);
+    extensionVisitor.visitModuleName(moduleName);
     if (anonymousObjectOrigin != null) {
       rewritten |=
           anonymousObjectOrigin.toRenamedBinaryNameOrDefault(
               renamedAnon -> {
                 if (renamedAnon != null) {
-                  JvmExtensionsKt.setAnonymousObjectOriginName(kmClass, renamedAnon);
+                  extensionVisitor.visitAnonymousObjectOriginName(renamedAnon);
                 }
               },
               appView,
               null);
     }
     rewritten |=
-        localDelegatedProperties.rewrite(
-            JvmExtensionsKt.getLocalDelegatedProperties(kmClass)::add, appView);
+        localDelegatedProperties.rewrite(extensionVisitor::visitLocalDelegatedProperty, appView);
+    extensionVisitor.visitEnd();
+    KotlinClassMetadata.Class.Writer writer = new KotlinClassMetadata.Class.Writer();
+    kmClass.accept(writer);
     return Pair.create(
-        Companion.writeClass(kmClass, getCompatibleKotlinInfo(), 0).getAnnotationData(),
+        writer.write().getHeader(),
         rewritten || !originalMembersWithKotlinInfo.isEqual(rewrittenReferences, appView));
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
index d634e88..e8f7fd0 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Pair;
-import kotlin.Metadata;
+import kotlinx.metadata.jvm.KotlinClassHeader;
 
 public interface KotlinClassLevelInfo extends EnqueuerMetadataTraceable {
 
@@ -56,7 +56,7 @@
     return null;
   }
 
-  Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView);
+  Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView);
 
   String getPackageName();
 
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 7bae38e..eb07f4b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -21,8 +21,8 @@
 import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.function.Consumer;
-import kotlin.Metadata;
 import kotlinx.metadata.InconsistentKotlinMetadataException;
+import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade;
 import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassFacade;
@@ -121,8 +121,8 @@
     Integer xi = extraInt == null ? null : (Integer) extraInt.value.getBoxedValue();
 
     try {
-      return KotlinClassMetadata.read(
-          new KotlinMetadataAnnotationWrapper(k, mv, d1, d2, xs, pn, xi));
+      KotlinClassHeader header = new KotlinClassHeader(k, mv, d1, d2, xs, pn, xi);
+      return KotlinClassMetadata.read(header);
     } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
       throw new KotlinMetadataException(e);
     }
@@ -142,9 +142,8 @@
       KotlinClassMetadata kMetadata,
       AppView<?> appView,
       Consumer<DexEncodedMethod> keepByteCode) {
-    Metadata annotationData = kMetadata.getAnnotationData();
-    String packageName = annotationData.pn();
-    int[] metadataVersion = annotationData.mv();
+    String packageName = kMetadata.getHeader().getPackageName();
+    int[] metadataVersion = kMetadata.getHeader().getMetadataVersion();
     if (kMetadata instanceof KotlinClassMetadata.Class) {
       return KotlinClassInfo.create(
           (KotlinClassMetadata.Class) kMetadata,
@@ -179,7 +178,7 @@
           kotlin,
           appView);
     } else {
-      throw new MetadataError("unsupported 'k' value: " + annotationData.k());
+      throw new MetadataError("unsupported 'k' value: " + kMetadata.getHeader().getKind());
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
index c8dac29..4c93733 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
@@ -68,13 +67,10 @@
               method,
               appView);
     }
-    rewritten |=
-        rewriteList(
-            appView,
-            valueParameters,
-            kmConstructor.getValueParameters(),
-            KotlinValueParameterInfo::rewrite);
-    rewritten |= versionRequirements.rewrite(kmConstructor.getVersionRequirements()::addAll);
+    for (KotlinValueParameterInfo valueParameterInfo : valueParameters) {
+      rewritten |= valueParameterInfo.rewrite(kmConstructor::visitValueParameter, appView);
+    }
+    rewritten |= versionRequirements.rewrite(kmConstructor::visitVersionRequirement);
     kmClass.getConstructors().add(kmConstructor);
     return rewritten;
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
index 57466ae..c437bba 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
@@ -15,8 +13,8 @@
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmContract;
+import kotlinx.metadata.KmContractVisitor;
 import kotlinx.metadata.KmEffect;
 
 public class KotlinContractInfo implements EnqueuerMetadataTraceable {
@@ -50,11 +48,17 @@
     forEachApply(effects, effect -> effect::trace, definitionSupplier);
   }
 
-  boolean rewrite(Consumer<KmContract> consumer, AppView<?> appView) {
+  boolean rewrite(
+      KmVisitorProviders.KmContractVisitorProvider visitorProvider, AppView<?> appView) {
     if (this == NO_EFFECT) {
       return false;
     }
-    KmContract kmContract = consume(new KmContract(), consumer);
-    return rewriteList(appView, effects, kmContract.getEffects(), KotlinEffectInfo::rewrite);
+    boolean rewritten = false;
+    KmContractVisitor kmContractVisitor = visitorProvider.get();
+    for (KotlinEffectInfo effect : effects) {
+      rewritten |= effect.rewrite(kmContractVisitor::visitEffect, appView);
+    }
+    kmContractVisitor.visitEnd();
+    return rewritten;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index 9abed29..9307053 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.isValidMethodDescriptor;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toDefaultJvmMethodSignature;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
@@ -180,15 +179,17 @@
   }
 
   boolean rewrite(
-      Consumer<KmFunction> functionConsumer,
-      Consumer<KmProperty> propertyConsumer,
-      Consumer<KmTypeAlias> typeAliasConsumer,
+      KmVisitorProviders.KmFunctionVisitorProvider functionProvider,
+      KmVisitorProviders.KmPropertyVisitorProvider propertyProvider,
+      KmVisitorProviders.KmTypeAliasVisitorProvider typeAliasProvider,
       DexClass clazz,
       AppView<?> appView,
       KotlinMetadataMembersTracker rewrittenMembersWithKotlinInfo) {
     // Type aliases only have a representation here, so we can generate them directly.
-    boolean rewritten =
-        rewriteList(appView, typeAliases, typeAliasConsumer, KotlinTypeAliasInfo::rewrite);
+    boolean rewritten = false;
+    for (KotlinTypeAliasInfo typeAlias : typeAliases) {
+      rewritten |= typeAlias.rewrite(typeAliasProvider, appView);
+    }
     // For properties, we need to combine potentially a field, setter and getter.
     Map<KotlinPropertyInfo, KotlinPropertyGroup> properties = new LinkedHashMap<>();
     for (DexEncodedField field : clazz.fields()) {
@@ -202,7 +203,7 @@
     }
     for (DexEncodedMethod method : clazz.methods()) {
       if (method.getKotlinInfo().isFunction()) {
-        rewritten |= method.getKotlinInfo().asFunction().rewrite(functionConsumer, method, appView);
+        rewritten |= method.getKotlinInfo().asFunction().rewrite(functionProvider, method, appView);
         rewrittenMembersWithKotlinInfo.add(method.getReference());
         continue;
       }
@@ -224,25 +225,19 @@
       KotlinPropertyGroup kotlinPropertyGroup = properties.get(kotlinPropertyInfo);
       rewritten |=
           kotlinPropertyInfo.rewrite(
-              propertyConsumer,
+              propertyProvider,
               kotlinPropertyGroup.backingField,
               kotlinPropertyGroup.getter,
               kotlinPropertyGroup.setter,
               appView);
     }
     // Add all not backed functions and properties.
-    rewritten |=
-        rewriteList(
-            appView,
-            functionsWithNoBacking,
-            functionConsumer,
-            KotlinFunctionInfo::rewriteNoBacking);
-    rewritten |=
-        rewriteList(
-            appView,
-            propertiesWithNoBacking,
-            propertyConsumer,
-            KotlinPropertyInfo::rewriteNoBacking);
+    for (KotlinFunctionInfo notBackedFunction : functionsWithNoBacking) {
+      rewritten |= notBackedFunction.rewrite(functionProvider, null, appView);
+    }
+    for (KotlinPropertyInfo notBackedProperty : propertiesWithNoBacking) {
+      rewritten |= notBackedProperty.rewrite(propertyProvider, null, null, null, appView);
+    }
     return rewritten;
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
index 4268f32..18afe26 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
@@ -4,21 +4,19 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.kotlin.KmVisitorProviders.KmEffectExpressionVisitorProvider;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmConstantValue;
 import kotlinx.metadata.KmEffectExpression;
+import kotlinx.metadata.KmEffectExpressionVisitor;
 
 public class KotlinEffectExpressionInfo implements EnqueuerMetadataTraceable {
 
@@ -86,31 +84,26 @@
     forEachApply(orArguments, arg -> arg::trace, definitionSupplier);
   }
 
-  boolean rewrite(Consumer<KmEffectExpression> consumer, AppView<?> appView) {
+  boolean rewrite(KmEffectExpressionVisitorProvider provider, AppView<?> appView) {
     if (this == NO_EXPRESSION) {
       return false;
     }
-    KmEffectExpression effectExpression = consume(new KmEffectExpression(), consumer);
-    effectExpression.setFlags(flags);
-    effectExpression.setParameterIndex(parameterIndex);
+    KmEffectExpressionVisitor visitor = provider.get();
+    visitor.visit(flags, parameterIndex);
     if (constantValue != null) {
-      effectExpression.setConstantValue(constantValue);
+      visitor.visitConstantValue(constantValue.getValue());
     }
-    boolean rewritten =
-        rewriteIfNotNull(
-            appView, isInstanceType, effectExpression::setInstanceType, KotlinTypeInfo::rewrite);
-    rewritten |=
-        rewriteList(
-            appView,
-            andArguments,
-            effectExpression.getAndArguments(),
-            KotlinEffectExpressionInfo::rewrite);
-    rewritten |=
-        rewriteList(
-            appView,
-            orArguments,
-            effectExpression.getOrArguments(),
-            KotlinEffectExpressionInfo::rewrite);
+    boolean rewritten = false;
+    if (isInstanceType != null) {
+      rewritten |= isInstanceType.rewrite(visitor::visitIsInstanceType, appView);
+    }
+    for (KotlinEffectExpressionInfo andArgument : andArguments) {
+      rewritten |= andArgument.rewrite(visitor::visitAndArgument, appView);
+    }
+    for (KotlinEffectExpressionInfo orArgument : orArguments) {
+      rewritten |= orArgument.rewrite(visitor::visitAndArgument, appView);
+    }
+    visitor.visitEnd();
     return rewritten;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
index 4f38113..e93b126 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
@@ -4,20 +4,19 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.kotlin.KmVisitorProviders.KmEffectVisitorProvider;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmEffect;
 import kotlinx.metadata.KmEffectInvocationKind;
 import kotlinx.metadata.KmEffectType;
+import kotlinx.metadata.KmEffectVisitor;
 
 public class KotlinEffectInfo implements EnqueuerMetadataTraceable {
 
@@ -51,15 +50,14 @@
     conclusion.trace(definitionSupplier);
   }
 
-  boolean rewrite(Consumer<KmEffect> consumer, AppView<?> appView) {
-    KmEffect kmEffect = consume(new KmEffect(type, invocationKind), consumer);
-    boolean rewritten = conclusion.rewrite(kmEffect::setConclusion, appView);
-    rewritten |=
-        rewriteList(
-            appView,
-            constructorArguments,
-            kmEffect.getConstructorArguments(),
-            KotlinEffectExpressionInfo::rewrite);
+  boolean rewrite(KmEffectVisitorProvider visitorProvider, AppView<?> appView) {
+    KmEffectVisitor kmEffectVisitor = visitorProvider.get(type, invocationKind);
+    boolean rewritten =
+        conclusion.rewrite(kmEffectVisitor::visitConclusionOfConditionalEffect, appView);
+    for (KotlinEffectExpressionInfo constructorArgument : constructorArguments) {
+      rewritten |= constructorArgument.rewrite(kmEffectVisitor::visitConstructorArgument, appView);
+    }
+    kmEffectVisitor.visitEnd();
     return rewritten;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
index 59e482f..3daa33d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -4,17 +4,15 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
-import static kotlinx.metadata.jvm.KotlinClassMetadata.Companion;
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.utils.Pair;
 import java.util.function.Consumer;
-import kotlin.Metadata;
 import kotlinx.metadata.KmPackage;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
 import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade;
 
 // Holds information about Metadata.FileFacade
@@ -59,12 +57,12 @@
   }
 
   @Override
-  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
     KmPackage kmPackage = new KmPackage();
     boolean rewritten = packageInfo.rewrite(kmPackage, clazz, appView);
-    return Pair.create(
-        Companion.writeFileFacade(kmPackage, getCompatibleKotlinInfo(), 0).getAnnotationData(),
-        rewritten);
+    KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
+    kmPackage.accept(writer);
+    return Pair.create(writer.write().getHeader(), rewritten);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
index 1edbe51..35e52f5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmFlexibleTypeUpperBound;
 
 public class KotlinFlexibleTypeUpperBoundInfo implements EnqueuerMetadataTraceable {
@@ -38,7 +37,8 @@
         KotlinTypeInfo.create(flexibleTypeUpperBound.getType(), factory, reporter));
   }
 
-  boolean rewrite(Consumer<KmFlexibleTypeUpperBound> consumer, AppView<?> appView) {
+  boolean rewrite(
+      KmVisitorProviders.KmFlexibleUpperBoundVisitorProvider visitorProvider, AppView<?> appView) {
     if (this == NO_FLEXIBLE_UPPER_BOUND) {
       // Nothing to do.
       return false;
@@ -47,9 +47,7 @@
       assert false;
       return false;
     }
-    return kotlinTypeInfo.rewrite(
-        kmType -> consumer.accept(new KmFlexibleTypeUpperBound(kmType, typeFlexibilityId)),
-        appView);
+    return kotlinTypeInfo.rewrite(flags -> visitorProvider.get(flags, typeFlexibilityId), appView);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
index 351acc9..69e4004 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
@@ -16,9 +13,10 @@
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmFunctionVisitor;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor;
 
 // Holds information about KmFunction
 public final class KotlinFunctionInfo implements KotlinMethodLevelInfo {
@@ -125,11 +123,10 @@
     return name;
   }
 
-  boolean rewriteNoBacking(Consumer<KmFunction> consumer, AppView<?> appView) {
-    return rewrite(consumer, null, appView);
-  }
-
-  boolean rewrite(Consumer<KmFunction> consumer, DexEncodedMethod method, AppView<?> appView) {
+  boolean rewrite(
+      KmVisitorProviders.KmFunctionVisitorProvider visitorProvider,
+      DexEncodedMethod method,
+      AppView<?> appView) {
     // TODO(b/154348683): Check method for flags to pass in.
     boolean rewritten = false;
     String finalName = name;
@@ -143,51 +140,39 @@
         finalName = rewrittenName;
       }
     }
-    KmFunction kmFunction = consume(new KmFunction(flags, finalName), consumer);
+    KmFunctionVisitor kmFunction = visitorProvider.get(flags, finalName);
     // TODO(b/154348149): ReturnType could have been merged to a subtype.
-    rewritten |= returnType.rewrite(kmFunction::setReturnType, appView);
-    rewritten |=
-        rewriteList(
-            appView,
-            valueParameters,
-            kmFunction.getValueParameters(),
-            KotlinValueParameterInfo::rewrite);
-    rewritten |=
-        rewriteList(
-            appView,
-            typeParameters,
-            kmFunction.getTypeParameters(),
-            KotlinTypeParameterInfo::rewrite);
-    rewritten |=
-        rewriteList(
-            appView,
-            contextReceiverTypes,
-            kmFunction.getContextReceiverTypes(),
-            KotlinTypeInfo::rewrite);
-    rewritten |=
-        rewriteIfNotNull(
-            appView,
-            receiverParameterType,
-            kmFunction::setReceiverParameterType,
-            KotlinTypeInfo::rewrite);
-    rewritten |= versionRequirements.rewrite(kmFunction.getVersionRequirements()::addAll);
-    if (signature != null) {
-      rewritten |=
-          signature.rewrite(
-              signature -> JvmExtensionsKt.setSignature(kmFunction, signature), method, appView);
+    rewritten |= returnType.rewrite(kmFunction::visitReturnType, appView);
+    for (KotlinValueParameterInfo valueParameterInfo : valueParameters) {
+      rewritten |= valueParameterInfo.rewrite(kmFunction::visitValueParameter, appView);
     }
-    if (lambdaClassOrigin != null) {
+    for (KotlinTypeParameterInfo typeParameterInfo : typeParameters) {
+      rewritten |= typeParameterInfo.rewrite(kmFunction::visitTypeParameter, appView);
+    }
+    for (KotlinTypeInfo contextReceiverType : contextReceiverTypes) {
+      rewritten |= contextReceiverType.rewrite(kmFunction::visitContextReceiverType, appView);
+    }
+    if (receiverParameterType != null) {
+      rewritten |= receiverParameterType.rewrite(kmFunction::visitReceiverParameterType, appView);
+    }
+    rewritten |= versionRequirements.rewrite(kmFunction::visitVersionRequirement);
+    JvmFunctionExtensionVisitor extensionVisitor =
+        (JvmFunctionExtensionVisitor) kmFunction.visitExtensions(JvmFunctionExtensionVisitor.TYPE);
+    if (signature != null && extensionVisitor != null) {
+      rewritten |= signature.rewrite(extensionVisitor::visit, method, appView);
+    }
+    if (lambdaClassOrigin != null && extensionVisitor != null) {
       rewritten |=
           lambdaClassOrigin.toRenamedBinaryNameOrDefault(
               lambdaClassOriginName -> {
                 if (lambdaClassOriginName != null) {
-                  JvmExtensionsKt.setLambdaClassOriginName(kmFunction, lambdaClassOriginName);
+                  extensionVisitor.visitLambdaClassOriginName(lambdaClassOriginName);
                 }
               },
               appView,
               null);
     }
-    rewritten |= contract.rewrite(kmFunction::setContract, appView);
+    rewritten |= contract.rewrite(kmFunction::visitContract, appView);
     return rewritten;
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
index 2c5e817..2760ece 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -70,10 +70,6 @@
     return new KotlinJvmMethodSignatureInfo(name, returnType, parameters.build());
   }
 
-  boolean rewriteNoBacking(Consumer<JvmMethodSignature> consumer, AppView<?> appView) {
-    return rewrite(consumer, null, appView);
-  }
-
   boolean rewrite(
       Consumer<JvmMethodSignature> consumer, DexEncodedMethod method, AppView<?> appView) {
     if (invalidDescriptor != null) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
index 9900e81..ffe08b4 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
 
 import com.android.tools.r8.graph.AppView;
@@ -14,7 +13,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmLambda;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
 import kotlinx.metadata.jvm.JvmMethodSignature;
@@ -58,10 +56,12 @@
     return new KotlinLambdaInfo(kotlinFunctionInfo, false);
   }
 
-  boolean rewrite(Consumer<KmLambda> consumer, DexClass clazz, AppView<?> appView) {
-    KmLambda kmLambda = consume(new KmLambda(), consumer);
+  boolean rewrite(
+      KmVisitorProviders.KmLambdaVisitorProvider visitorProvider,
+      DexClass clazz,
+      AppView<?> appView) {
     if (!hasBacking) {
-      function.rewrite(kmLambda::setFunction, null, appView);
+      function.rewrite(visitorProvider.get()::visitFunction, null, appView);
       return true;
     }
     DexEncodedMethod backing = null;
@@ -71,7 +71,7 @@
         break;
       }
     }
-    return function.rewrite(kmLambda::setFunction, backing, appView);
+    return function.rewrite(visitorProvider.get()::visitFunction, backing, appView);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
index 9fb8b43..4bdce09 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
@@ -4,17 +4,16 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.kotlin.KmVisitorProviders.KmPropertyVisitorProvider;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmProperty;
 
 public class KotlinLocalDelegatedPropertyInfo implements EnqueuerMetadataTraceable {
@@ -52,7 +51,11 @@
     forEachApply(propertyInfos, prop -> prop::trace, definitionSupplier);
   }
 
-  boolean rewrite(Consumer<KmProperty> consumer, AppView<?> appView) {
-    return rewriteList(appView, propertyInfos, consumer, KotlinPropertyInfo::rewriteNoBacking);
+  boolean rewrite(KmPropertyVisitorProvider visitorProvider, AppView<?> appView) {
+    boolean rewritten = false;
+    for (KotlinPropertyInfo propertyInfo : propertyInfos) {
+      rewritten |= propertyInfo.rewrite(visitorProvider, null, null, null, appView);
+    }
+    return rewritten;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java
deleted file mode 100644
index efbe154..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2023, 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.kotlin;
-
-import com.android.tools.r8.errors.Unreachable;
-import java.lang.annotation.Annotation;
-import kotlin.Metadata;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
-
-/***
- * This is a wrapper around kotlin.Metadata needed for tests to access the internal data. The need
- * for the wrapper comes from R8 relocating kotlin.* to com.android.tools.r8.kotlin.* in R8lib but
- * not in tests, so kotlin.Metadata cannot cross the boundary.
- *
- * Additionally, it is also used for passing in an instance of kotlin.Metadata which cannot be
- * instantiated from Java.
- */
-public class KotlinMetadataAnnotationWrapper implements kotlin.Metadata {
-
-  private static final String[] NULL_STRING_ARRAY = new String[0];
-  private static final int[] NULL_INT_ARRAY = new int[0];
-
-  private final int kind;
-  private final int[] metadataVersion;
-  private final String[] data1;
-  private final String[] data2;
-  private final int extraInt;
-  private final String extraString;
-  private final String packageName;
-
-  public KotlinMetadataAnnotationWrapper(
-      Integer kind,
-      int[] metadataVersion,
-      String[] data1,
-      String[] data2,
-      String extraString,
-      String packageName,
-      Integer extraInt) {
-    // The default values here are taking from the constructor of KotlinClassHeader.
-    this.kind = kind == null ? 1 : kind;
-    this.metadataVersion = metadataVersion == null ? NULL_INT_ARRAY : metadataVersion;
-    this.data1 = data1 == null ? NULL_STRING_ARRAY : data1;
-    this.data2 = data2 == null ? NULL_STRING_ARRAY : data2;
-    this.extraString = extraString == null ? "" : extraString;
-    this.packageName = packageName == null ? "" : packageName;
-    this.extraInt = extraInt == null ? 0 : extraInt;
-  }
-
-  public static KotlinMetadataAnnotationWrapper wrap(KotlinClassMetadata classMetadata) {
-    Metadata annotationData = classMetadata.getAnnotationData();
-    return new KotlinMetadataAnnotationWrapper(
-        annotationData.k(),
-        annotationData.mv(),
-        annotationData.d1(),
-        annotationData.d2(),
-        annotationData.xs(),
-        annotationData.pn(),
-        annotationData.xi());
-  }
-
-  public int kind() {
-    return kind;
-  }
-
-  public String[] data1() {
-    return data1;
-  }
-
-  public String[] data2() {
-    return data2;
-  }
-
-  public String packageName() {
-    return pn();
-  }
-
-  @Override
-  public int[] bv() {
-    throw new Unreachable("Field is deprecated and should not be used");
-  }
-
-  @Override
-  public String[] d1() {
-    return data1;
-  }
-
-  @Override
-  public String[] d2() {
-    return data2;
-  }
-
-  @Override
-  public int xi() {
-    return extraInt;
-  }
-
-  @Override
-  public String xs() {
-    return extraString;
-  }
-
-  @Override
-  public int k() {
-    return kind;
-  }
-
-  @Override
-  public int[] mv() {
-    return metadataVersion;
-  }
-
-  @Override
-  public String pn() {
-    return packageName;
-  }
-
-  @Override
-  public Class<? extends Annotation> annotationType() {
-    throw new Unreachable("Should never be called");
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index e57fc63..c06aea8 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -27,6 +27,7 @@
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import kotlinx.metadata.jvm.KotlinClassHeader;
 
 public class KotlinMetadataRewriter {
 
@@ -154,16 +155,17 @@
       DexAnnotation oldMeta,
       WriteMetadataFieldInfo writeMetadataFieldInfo) {
     try {
-      Pair<kotlin.Metadata, Boolean> kotlinMetadata = kotlinInfo.rewrite(clazz, appView);
+      Pair<KotlinClassHeader, Boolean> kotlinClassHeader = kotlinInfo.rewrite(clazz, appView);
       // TODO(b/185756596): Remove when special handling is no longer needed.
-      if (!kotlinMetadata.getSecond() && appView.options().testing.keepMetadataInR8IfNotRewritten) {
+      if (!kotlinClassHeader.getSecond()
+          && appView.options().testing.keepMetadataInR8IfNotRewritten) {
         // No rewrite occurred and the data is the same as before.
         assert appView.checkForTesting(
             () ->
                 verifyRewrittenMetadataIsEquivalent(
                     clazz.annotations().getFirstMatching(factory.kotlinMetadataType),
                     createKotlinMetadataAnnotation(
-                        kotlinMetadata.getFirst(),
+                        kotlinClassHeader.getFirst(),
                         kotlinInfo.getPackageName(),
                         getMaxVersion(METADATA_VERSION_1_4, kotlinInfo.getMetadataVersion()),
                         writeMetadataFieldInfo)));
@@ -171,7 +173,7 @@
       }
       DexAnnotation newMeta =
           createKotlinMetadataAnnotation(
-              kotlinMetadata.getFirst(),
+              kotlinClassHeader.getFirst(),
               kotlinInfo.getPackageName(),
               getMaxVersion(METADATA_VERSION_1_4, kotlinInfo.getMetadataVersion()),
               writeMetadataFieldInfo);
@@ -220,7 +222,7 @@
   }
 
   private DexAnnotation createKotlinMetadataAnnotation(
-      kotlin.Metadata metadata,
+      KotlinClassHeader header,
       String packageName,
       int[] metadataVersion,
       WriteMetadataFieldInfo writeMetadataFieldInfo) {
@@ -232,30 +234,31 @@
     }
     if (writeMetadataFieldInfo.writeKind) {
       elements.add(
-          new DexAnnotationElement(kotlin.metadata.kind, DexValueInt.create(metadata.k())));
+          new DexAnnotationElement(kotlin.metadata.kind, DexValueInt.create(header.getKind())));
     }
     if (writeMetadataFieldInfo.writeData1) {
       elements.add(
-          new DexAnnotationElement(kotlin.metadata.data1, createStringArray(metadata.d1())));
+          new DexAnnotationElement(kotlin.metadata.data1, createStringArray(header.getData1())));
     }
     if (writeMetadataFieldInfo.writeData2) {
       elements.add(
-          new DexAnnotationElement(kotlin.metadata.data2, createStringArray(metadata.d2())));
+          new DexAnnotationElement(kotlin.metadata.data2, createStringArray(header.getData2())));
     }
     if (writeMetadataFieldInfo.writePackageName && packageName != null && !packageName.isEmpty()) {
       elements.add(
           new DexAnnotationElement(
               kotlin.metadata.packageName, new DexValueString(factory.createString(packageName))));
     }
-    if (writeMetadataFieldInfo.writeExtraString && !metadata.xs().isEmpty()) {
+    if (writeMetadataFieldInfo.writeExtraString && !header.getExtraString().isEmpty()) {
       elements.add(
           new DexAnnotationElement(
               kotlin.metadata.extraString,
-              new DexValueString(factory.createString(metadata.xs()))));
+              new DexValueString(factory.createString(header.getExtraString()))));
     }
-    if (writeMetadataFieldInfo.writeExtraInt && metadata.xi() != 0) {
+    if (writeMetadataFieldInfo.writeExtraInt && header.getExtraInt() != 0) {
       elements.add(
-          new DexAnnotationElement(kotlin.metadata.extraInt, DexValueInt.create(metadata.xi())));
+          new DexAnnotationElement(
+              kotlin.metadata.extraInt, DexValueInt.create(header.getExtraInt())));
     }
     DexEncodedAnnotation encodedAnnotation =
         new DexEncodedAnnotation(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index 67a900a..d5884f5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -20,11 +20,7 @@
 import com.android.tools.r8.shaking.ProguardKeepRuleType;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.TriFunction;
 import com.google.common.base.Strings;
-import java.util.List;
-import java.util.function.Consumer;
-import kotlin.Metadata;
 import kotlinx.metadata.KmExtensionType;
 import kotlinx.metadata.KmProperty;
 import kotlinx.metadata.KmPropertyExtensionVisitor;
@@ -32,7 +28,7 @@
 import kotlinx.metadata.jvm.JvmFieldSignature;
 import kotlinx.metadata.jvm.JvmMethodSignature;
 import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassHeader;
 
 public class KotlinMetadataUtils {
 
@@ -54,7 +50,7 @@
     }
 
     @Override
-    public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+    public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
       throw new Unreachable("Should never be called");
     }
 
@@ -236,42 +232,4 @@
     }
     return DescriptorUtils.descriptorToKotlinClassifier(descriptor);
   }
-
-  static int[] getCompatibleKotlinInfo() {
-    return KotlinClassMetadata.COMPATIBLE_METADATA_VERSION;
-  }
-
-  static <TKm> TKm consume(TKm tKm, Consumer<TKm> consumer) {
-    consumer.accept(tKm);
-    return tKm;
-  }
-
-  static <TInfo, TKm> boolean rewriteIfNotNull(
-      AppView<?> appView,
-      TInfo info,
-      Consumer<TKm> newTConsumer,
-      TriFunction<TInfo, Consumer<TKm>, AppView<?>, Boolean> rewrite) {
-    return info != null ? rewrite.apply(info, newTConsumer, appView) : false;
-  }
-
-  static <TInfo, TKm> boolean rewriteList(
-      AppView<?> appView,
-      List<TInfo> ts,
-      List<TKm> newTs,
-      TriFunction<TInfo, Consumer<TKm>, AppView<?>, Boolean> rewrite) {
-    assert newTs.isEmpty();
-    return rewriteList(appView, ts, newTs::add, rewrite);
-  }
-
-  static <TInfo, TKm> boolean rewriteList(
-      AppView<?> appView,
-      List<TInfo> ts,
-      Consumer<TKm> newTConsumer,
-      TriFunction<TInfo, Consumer<TKm>, AppView<?>, Boolean> rewrite) {
-    boolean rewritten = false;
-    for (TInfo t : ts) {
-      rewritten |= rewrite.apply(t, newTConsumer, appView);
-    }
-    return rewritten;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
index 7f0b67f..2a89998 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
@@ -4,9 +4,7 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
-import static kotlinx.metadata.jvm.KotlinClassMetadata.Companion;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -16,7 +14,8 @@
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.List;
-import kotlin.Metadata;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
 import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassFacade;
 
 // Holds information about Metadata.MultiFileClassFace
@@ -56,7 +55,7 @@
   }
 
   @Override
-  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
     List<String> partClassNameStrings = new ArrayList<>(partClassNames.size());
     boolean rewritten = false;
     for (KotlinTypeReference partClassName : partClassNames) {
@@ -70,10 +69,9 @@
               appView,
               null);
     }
-    return Pair.create(
-        Companion.writeMultiFileClassFacade(partClassNameStrings, getCompatibleKotlinInfo(), 0)
-            .getAnnotationData(),
-        rewritten);
+    KotlinClassMetadata.MultiFileClassFacade.Writer writer =
+        new KotlinClassMetadata.MultiFileClassFacade.Writer();
+    return Pair.create(writer.write(partClassNameStrings).getHeader(), rewritten);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
index a51defc..62460ae 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -4,17 +4,15 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
-import static kotlinx.metadata.jvm.KotlinClassMetadata.Companion;
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.utils.Pair;
 import java.util.function.Consumer;
-import kotlin.Metadata;
 import kotlinx.metadata.KmPackage;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
 import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassPart;
 
 // Holds information about Metadata.MultiFileClassPartInfo
@@ -65,13 +63,13 @@
   }
 
   @Override
-  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
     KmPackage kmPackage = new KmPackage();
     boolean rewritten = packageInfo.rewrite(kmPackage, clazz, appView);
-    return Pair.create(
-        Companion.writeMultiFileClassPart(kmPackage, facadeClassName, getCompatibleKotlinInfo(), 0)
-            .getAnnotationData(),
-        rewritten);
+    KotlinClassMetadata.MultiFileClassPart.Writer writer =
+        new KotlinClassMetadata.MultiFileClassPart.Writer();
+    kmPackage.accept(writer);
+    return Pair.create(writer.write(facadeClassName).getHeader(), rewritten);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index 721cd75..a0f0e81 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -18,6 +18,7 @@
 import java.util.function.Consumer;
 import kotlinx.metadata.KmPackage;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmPackageExtensionVisitor;
 
 // Holds information about a KmPackage object.
 public class KotlinPackageInfo implements EnqueuerMetadataTraceable {
@@ -76,16 +77,18 @@
     KotlinMetadataMembersTracker rewrittenReferences = new KotlinMetadataMembersTracker(appView);
     boolean rewritten =
         containerInfo.rewrite(
-            kmPackage.getFunctions()::add,
-            kmPackage.getProperties()::add,
-            kmPackage.getTypeAliases()::add,
+            kmPackage::visitFunction,
+            kmPackage::visitProperty,
+            kmPackage::visitTypeAlias,
             clazz,
             appView,
             rewrittenReferences);
+    JvmPackageExtensionVisitor extensionVisitor =
+        (JvmPackageExtensionVisitor) kmPackage.visitExtensions(JvmPackageExtensionVisitor.TYPE);
     rewritten |=
-        localDelegatedProperties.rewrite(
-            JvmExtensionsKt.getLocalDelegatedProperties(kmPackage)::add, appView);
-    JvmExtensionsKt.setModuleName(kmPackage, moduleName);
+        localDelegatedProperties.rewrite(extensionVisitor::visitLocalDelegatedProperty, appView);
+    extensionVisitor.visitModuleName(moduleName);
+    extensionVisitor.visitEnd();
     return rewritten || !originalMembersWithKotlinInfo.isEqual(rewrittenReferences, appView);
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
index fc8e889..a11b9a9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
@@ -14,12 +11,16 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmPropertyVisitor;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
 
 // Holds information about KmProperty
 public class KotlinPropertyInfo implements KotlinFieldLevelInfo, KotlinMethodLevelInfo {
@@ -145,81 +146,63 @@
     return setterSignature;
   }
 
-  boolean rewriteNoBacking(Consumer<KmProperty> consumer, AppView<?> appView) {
-    return rewrite(consumer, null, null, null, appView);
-  }
-
   boolean rewrite(
-      Consumer<KmProperty> consumer,
+      KmVisitorProviders.KmPropertyVisitorProvider visitorProvider,
       DexEncodedField field,
       DexEncodedMethod getter,
       DexEncodedMethod setter,
       AppView<?> appView) {
     // TODO(b/154348683): Flags again.
-    KmProperty kmProperty =
-        consume(new KmProperty(flags, name, getterFlags, setterFlags), consumer);
+    KmPropertyVisitor kmProperty = visitorProvider.get(flags, name, getterFlags, setterFlags);
     // TODO(b/154348149): ReturnType could have been merged to a subtype.
-    boolean rewritten =
-        rewriteIfNotNull(appView, returnType, kmProperty::setReturnType, KotlinTypeInfo::rewrite);
-    rewritten |=
-        rewriteIfNotNull(
-            appView,
-            receiverParameterType,
-            kmProperty::setReceiverParameterType,
-            KotlinTypeInfo::rewrite);
-    rewritten |=
-        rewriteIfNotNull(
-            appView,
-            setterParameter,
-            kmProperty::setSetterParameter,
-            KotlinValueParameterInfo::rewrite);
-    rewritten |=
-        rewriteList(
-            appView,
-            typeParameters,
-            kmProperty.getTypeParameters(),
-            KotlinTypeParameterInfo::rewrite);
-    rewritten |=
-        rewriteList(
-            appView,
-            contextReceiverTypes,
-            kmProperty.getContextReceiverTypes(),
-            KotlinTypeInfo::rewrite);
-    rewritten |= versionRequirements.rewrite(kmProperty.getVersionRequirements()::addAll);
-    if (fieldSignature != null) {
-      rewritten |=
-          fieldSignature.rewrite(
-              newSignature -> JvmExtensionsKt.setFieldSignature(kmProperty, newSignature),
-              field,
-              appView);
+    boolean rewritten = false;
+    if (returnType != null) {
+      rewritten = returnType.rewrite(kmProperty::visitReturnType, appView);
     }
-    if (getterSignature != null) {
-      rewritten |=
-          getterSignature.rewrite(
-              newSignature -> JvmExtensionsKt.setGetterSignature(kmProperty, newSignature),
-              getter,
-              appView);
+    if (receiverParameterType != null) {
+      rewritten |= receiverParameterType.rewrite(kmProperty::visitReceiverParameterType, appView);
     }
-    if (setterSignature != null) {
-      rewritten |=
-          setterSignature.rewrite(
-              newSignature -> JvmExtensionsKt.setSetterSignature(kmProperty, newSignature),
-              setter,
-              appView);
+    if (setterParameter != null) {
+      rewritten |= setterParameter.rewrite(kmProperty::visitSetterParameter, appView);
     }
-    JvmExtensionsKt.setJvmFlags(kmProperty, jvmFlags);
-    rewritten |=
-        rewriteIfNotNull(
-            appView,
-            syntheticMethodForAnnotations,
-            newMethod -> JvmExtensionsKt.setSyntheticMethodForAnnotations(kmProperty, newMethod),
-            KotlinJvmMethodSignatureInfo::rewriteNoBacking);
-    rewritten |=
-        rewriteIfNotNull(
-            appView,
-            syntheticMethodForDelegate,
-            newMethod -> JvmExtensionsKt.setSyntheticMethodForDelegate(kmProperty, newMethod),
-            KotlinJvmMethodSignatureInfo::rewriteNoBacking);
+    for (KotlinTypeParameterInfo typeParameter : typeParameters) {
+      rewritten |= typeParameter.rewrite(kmProperty::visitTypeParameter, appView);
+    }
+    for (KotlinTypeInfo contextReceiverType : contextReceiverTypes) {
+      rewritten |= contextReceiverType.rewrite(kmProperty::visitContextReceiverType, appView);
+    }
+    rewritten |= versionRequirements.rewrite(kmProperty::visitVersionRequirement);
+    JvmPropertyExtensionVisitor extensionVisitor =
+        (JvmPropertyExtensionVisitor) kmProperty.visitExtensions(JvmPropertyExtensionVisitor.TYPE);
+    if (extensionVisitor != null) {
+      Box<JvmFieldSignature> rewrittenFieldSignature = new Box<>();
+      if (fieldSignature != null) {
+        rewritten |= fieldSignature.rewrite(rewrittenFieldSignature::set, field, appView);
+      }
+      Box<JvmMethodSignature> rewrittenGetterSignature = new Box<>();
+      if (getterSignature != null) {
+        rewritten |= getterSignature.rewrite(rewrittenGetterSignature::set, getter, appView);
+      }
+      Box<JvmMethodSignature> rewrittenSetterSignature = new Box<>();
+      if (setterSignature != null) {
+        rewritten |= setterSignature.rewrite(rewrittenSetterSignature::set, setter, appView);
+      }
+      extensionVisitor.visit(
+          jvmFlags,
+          rewrittenFieldSignature.get(),
+          rewrittenGetterSignature.get(),
+          rewrittenSetterSignature.get());
+      if (syntheticMethodForAnnotations != null) {
+        rewritten |=
+            syntheticMethodForAnnotations.rewrite(
+                extensionVisitor::visitSyntheticMethodForAnnotations, null, appView);
+      }
+      if (syntheticMethodForDelegate != null) {
+        rewritten |=
+            syntheticMethodForDelegate.rewrite(
+                extensionVisitor::visitSyntheticMethodForDelegate, null, appView);
+      }
+    }
     return rewritten;
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
index 2fb5dd1..fb9b55f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -4,17 +4,14 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getCompatibleKotlinInfo;
-import static kotlinx.metadata.jvm.KotlinClassMetadata.Companion;
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
-import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.Pair;
-import kotlin.Metadata;
 import kotlinx.metadata.KmLambda;
+import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
+import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass.Writer;
 
 // Holds information about a Metadata.SyntheticClass object.
 public class KotlinSyntheticClassInfo implements KotlinClassLevelInfo {
@@ -76,17 +73,15 @@
   }
 
   @Override
-  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
-    if (lambda == null) {
-      return Pair.create(
-          Companion.writeSyntheticClass(getCompatibleKotlinInfo(), 0).getAnnotationData(), false);
+  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+    Writer writer = new Writer();
+    boolean rewritten = false;
+    if (lambda != null) {
+      KmLambda kmLambda = new KmLambda();
+      rewritten = lambda.rewrite(() -> kmLambda, clazz, appView);
+      kmLambda.accept(writer);
     }
-    Box<KmLambda> newLambda = new Box<>();
-    boolean rewritten = lambda.rewrite(newLambda::set, clazz, appView);
-    assert newLambda.isSet();
-    return Pair.create(
-        Companion.writeLambda(newLambda.get(), getCompatibleKotlinInfo(), 0).getAnnotationData(),
-        rewritten);
+    return Pair.create(writer.write().getHeader(), rewritten);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
index 6bb9e59..8f89b9e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
@@ -14,8 +12,8 @@
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmTypeAlias;
+import kotlinx.metadata.KmTypeAliasVisitor;
 
 // Holds information about KmTypeAlias
 public class KotlinTypeAliasInfo implements EnqueuerMetadataTraceable {
@@ -59,20 +57,18 @@
         KotlinVersionRequirementInfo.create(alias.getVersionRequirements()));
   }
 
-  boolean rewrite(Consumer<KmTypeAlias> consumer, AppView<?> appView) {
-    KmTypeAlias kmTypeAlias = consume(new KmTypeAlias(flags, name), consumer);
-    boolean rewritten = underlyingType.rewrite(kmTypeAlias::setUnderlyingType, appView);
-    rewritten |= expandedType.rewrite(kmTypeAlias::setExpandedType, appView);
-    rewritten |=
-        rewriteList(
-            appView,
-            typeParameters,
-            kmTypeAlias.getTypeParameters(),
-            KotlinTypeParameterInfo::rewrite);
-    rewritten |=
-        rewriteList(
-            appView, annotations, kmTypeAlias.getAnnotations(), KotlinAnnotationInfo::rewrite);
-    rewritten |= versionRequirements.rewrite(kmTypeAlias.getVersionRequirements()::addAll);
+  boolean rewrite(
+      KmVisitorProviders.KmTypeAliasVisitorProvider visitorProvider, AppView<?> appView) {
+    KmTypeAliasVisitor kmTypeAliasVisitor = visitorProvider.get(flags, name);
+    boolean rewritten = underlyingType.rewrite(kmTypeAliasVisitor::visitUnderlyingType, appView);
+    rewritten |= expandedType.rewrite(kmTypeAliasVisitor::visitExpandedType, appView);
+    for (KotlinTypeParameterInfo typeParameter : typeParameters) {
+      rewritten |= typeParameter.rewrite(kmTypeAliasVisitor::visitTypeParameter, appView);
+    }
+    for (KotlinAnnotationInfo annotation : annotations) {
+      rewritten |= annotation.rewrite(kmTypeAliasVisitor::visitAnnotation, appView);
+    }
+    rewritten |= versionRequirements.rewrite(kmTypeAliasVisitor::visitVersionRequirement);
     return rewritten;
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
index 93735f4..a20d86c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
@@ -18,10 +15,11 @@
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmType;
 import kotlinx.metadata.KmTypeProjection;
+import kotlinx.metadata.KmTypeVisitor;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmTypeExtensionVisitor;
 
 // Provides access to Kotlin information about a kotlin type.
 public class KotlinTypeInfo implements EnqueuerMetadataTraceable {
@@ -84,28 +82,34 @@
     return arguments.build();
   }
 
-  boolean rewrite(Consumer<KmType> consumer, AppView<?> appView) {
+  boolean rewrite(KmVisitorProviders.KmTypeVisitorProvider visitorProvider, AppView<?> appView) {
     // TODO(b/154348683): Check for correct flags
-    KmType kmType = consume(new KmType(flags), consumer);
-    boolean rewritten = classifier.rewrite(kmType, appView);
+    KmTypeVisitor kmTypeVisitor = visitorProvider.get(flags);
+    boolean rewritten = classifier.rewrite(kmTypeVisitor, appView);
+    if (abbreviatedType != null) {
+      rewritten |= abbreviatedType.rewrite(kmTypeVisitor::visitAbbreviatedType, appView);
+    }
+    if (outerType != null) {
+      rewritten |= outerType.rewrite(kmTypeVisitor::visitOuterType, appView);
+    }
+    for (KotlinTypeProjectionInfo argument : arguments) {
+      rewritten |=
+          argument.rewrite(
+              kmTypeVisitor::visitArgument, kmTypeVisitor::visitStarProjection, appView);
+    }
     rewritten |=
-        rewriteIfNotNull(
-            appView, abbreviatedType, kmType::setAbbreviatedType, KotlinTypeInfo::rewrite);
-    rewritten |=
-        rewriteIfNotNull(appView, outerType, kmType::setOuterType, KotlinTypeInfo::rewrite);
-    rewritten |=
-        rewriteList(appView, arguments, kmType.getArguments(), KotlinTypeProjectionInfo::rewrite);
-    rewritten |= flexibleTypeUpperBound.rewrite(kmType::setFlexibleTypeUpperBound, appView);
+        flexibleTypeUpperBound.rewrite(kmTypeVisitor::visitFlexibleTypeUpperBound, appView);
     if (annotations.isEmpty() && !isRaw) {
       return rewritten;
     }
-    rewritten |=
-        rewriteList(
-            appView,
-            annotations,
-            JvmExtensionsKt.getAnnotations(kmType),
-            KotlinAnnotationInfo::rewrite);
-    JvmExtensionsKt.setRaw(kmType, isRaw);
+    JvmTypeExtensionVisitor extensionVisitor =
+        (JvmTypeExtensionVisitor) kmTypeVisitor.visitExtensions(JvmTypeExtensionVisitor.TYPE);
+    if (extensionVisitor != null) {
+      for (KotlinAnnotationInfo annotation : annotations) {
+        rewritten |= annotation.rewrite(extensionVisitor::visitAnnotation, appView);
+      }
+      extensionVisitor.visit(isRaw);
+    }
     return rewritten;
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
index 03bfcb5..e6cc10f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteList;
 import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
 
 import com.android.tools.r8.graph.AppView;
@@ -15,11 +13,12 @@
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmType;
 import kotlinx.metadata.KmTypeParameter;
+import kotlinx.metadata.KmTypeParameterVisitor;
 import kotlinx.metadata.KmVariance;
 import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmTypeParameterExtensionVisitor;
 
 // Provides access to Kotlin information about a type-parameter.
 public class KotlinTypeParameterInfo implements EnqueuerMetadataTraceable {
@@ -84,21 +83,24 @@
     return builder.build();
   }
 
-  boolean rewrite(Consumer<KmTypeParameter> consumer, AppView<?> appView) {
-    KmTypeParameter kmTypeParameter =
-        consume(new KmTypeParameter(flags, name, id, variance), consumer);
-    boolean rewritten =
-        rewriteList(
-            appView,
-            originalUpperBounds,
-            kmTypeParameter.getUpperBounds(),
-            KotlinTypeInfo::rewrite);
-    rewritten |=
-        rewriteList(
-            appView,
-            annotations,
-            JvmExtensionsKt.getAnnotations(kmTypeParameter),
-            KotlinAnnotationInfo::rewrite);
+  boolean rewrite(
+      KmVisitorProviders.KmTypeParameterVisitorProvider visitorProvider, AppView<?> appView) {
+    KmTypeParameterVisitor kmTypeParameterVisitor = visitorProvider.get(flags, name, id, variance);
+    boolean rewritten = false;
+    for (KotlinTypeInfo originalUpperBound : originalUpperBounds) {
+      rewritten |= originalUpperBound.rewrite(kmTypeParameterVisitor::visitUpperBound, appView);
+    }
+    if (annotations.isEmpty()) {
+      return rewritten;
+    }
+    JvmTypeParameterExtensionVisitor extensionVisitor =
+        (JvmTypeParameterExtensionVisitor)
+            kmTypeParameterVisitor.visitExtensions(JvmTypeParameterExtensionVisitor.TYPE);
+    if (extensionVisitor != null) {
+      for (KotlinAnnotationInfo annotation : annotations) {
+        rewritten |= annotation.rewrite(extensionVisitor::visitAnnotation, appView);
+      }
+    }
     return rewritten;
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
index 5ac01e6..85791fe 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
 import com.android.tools.r8.utils.Reporter;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmTypeProjection;
 import kotlinx.metadata.KmVariance;
 
@@ -35,13 +34,15 @@
     return variance == null && typeInfo == null;
   }
 
-  boolean rewrite(Consumer<KmTypeProjection> consumer, AppView<?> appView) {
+  boolean rewrite(
+      KmVisitorProviders.KmTypeProjectionVisitorProvider visitorProvider,
+      KmVisitorProviders.KmTypeStarProjectionVisitorProvider starProjectionProvider,
+      AppView<?> appView) {
     if (isStarProjection()) {
-      consumer.accept(KmTypeProjection.STAR);
+      starProjectionProvider.get();
       return false;
     } else {
-      return typeInfo.rewrite(
-          kmType -> consumer.accept(new KmTypeProjection(variance, kmType)), appView);
+      return typeInfo.rewrite(flags -> visitorProvider.get(flags, variance), appView);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index 68a79f2..1e12db6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.consume;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.rewriteIfNotNull;
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -14,9 +11,9 @@
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.function.Consumer;
 import kotlinx.metadata.KmType;
 import kotlinx.metadata.KmValueParameter;
+import kotlinx.metadata.KmValueParameterVisitor;
 import kotlinx.metadata.internal.metadata.deserialization.Flags;
 
 // Provides access to Kotlin information about value parameter.
@@ -68,15 +65,14 @@
     return builder.build();
   }
 
-  boolean rewrite(Consumer<KmValueParameter> consumer, AppView<?> appView) {
-    KmValueParameter kmValueParameter = consume(new KmValueParameter(flags, name), consumer);
-    boolean rewritten = type.rewrite(kmValueParameter::setType, appView);
-    rewritten |=
-        rewriteIfNotNull(
-            appView,
-            varargElementType,
-            kmValueParameter::setVarargElementType,
-            KotlinTypeInfo::rewrite);
+  boolean rewrite(
+      KmVisitorProviders.KmValueParameterVisitorProvider visitorProvider, AppView<?> appView) {
+    KmValueParameterVisitor kmValueParameterVisitor = visitorProvider.get(flags, name);
+    boolean rewritten = type.rewrite(kmValueParameterVisitor::visitType, appView);
+    if (varargElementType != null) {
+      rewritten |=
+          varargElementType.rewrite(kmValueParameterVisitor::visitVarargElementType, appView);
+    }
     return rewritten;
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
index c82b039..3ab63f9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
@@ -6,17 +6,20 @@
 
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import java.util.function.Consumer;
+import kotlinx.metadata.KmVersion;
 import kotlinx.metadata.KmVersionRequirement;
+import kotlinx.metadata.KmVersionRequirementLevel;
+import kotlinx.metadata.KmVersionRequirementVersionKind;
+import kotlinx.metadata.KmVersionRequirementVisitor;
 
 class KotlinVersionRequirementInfo {
 
   private static final KotlinVersionRequirementInfo NO_VERSION_REQUIREMENTS =
       new KotlinVersionRequirementInfo(ImmutableList.of());
 
-  private final List<KmVersionRequirement> versionRequirements;
+  private final List<KotlinVersionRequirementPoint> versionRequirements;
 
-  private KotlinVersionRequirementInfo(List<KmVersionRequirement> versionRequirements) {
+  private KotlinVersionRequirementInfo(List<KotlinVersionRequirementPoint> versionRequirements) {
     this.versionRequirements = versionRequirements;
   }
 
@@ -24,14 +27,57 @@
     if (kmVersionRequirements.isEmpty()) {
       return NO_VERSION_REQUIREMENTS;
     }
-    return new KotlinVersionRequirementInfo(ImmutableList.copyOf(kmVersionRequirements));
+    ImmutableList.Builder<KotlinVersionRequirementPoint> builder = ImmutableList.builder();
+    for (KmVersionRequirement kmVersionRequirement : kmVersionRequirements) {
+      builder.add(KotlinVersionRequirementPoint.create(kmVersionRequirement));
+    }
+    return new KotlinVersionRequirementInfo(builder.build());
   }
 
-  boolean rewrite(Consumer<List<KmVersionRequirement>> consumer) {
+  boolean rewrite(KmVisitorProviders.KmVersionRequirementVisitorProvider visitorProvider) {
     if (this == NO_VERSION_REQUIREMENTS) {
       return false;
     }
-    consumer.accept(versionRequirements);
+    for (KotlinVersionRequirementPoint versionRequirement : versionRequirements) {
+      versionRequirement.rewrite(visitorProvider.get());
+    }
     return false;
   }
+
+  private static class KotlinVersionRequirementPoint {
+
+    private final Integer errorCode;
+    private final KmVersionRequirementVersionKind kind;
+    private final KmVersionRequirementLevel level;
+    private final String message;
+    private final KmVersion version;
+
+    private KotlinVersionRequirementPoint(
+        KmVersionRequirementVersionKind kind,
+        KmVersionRequirementLevel level,
+        Integer errorCode,
+        String message,
+        KmVersion version) {
+      this.errorCode = errorCode;
+      this.kind = kind;
+      this.level = level;
+      this.message = message;
+      this.version = version;
+    }
+
+    private static KotlinVersionRequirementPoint create(KmVersionRequirement kmVersionRequirement) {
+      return new KotlinVersionRequirementPoint(
+          kmVersionRequirement.kind,
+          kmVersionRequirement.level,
+          kmVersionRequirement.getErrorCode(),
+          kmVersionRequirement.getMessage(),
+          kmVersionRequirement.version);
+    }
+
+    private void rewrite(KmVersionRequirementVisitor visitor) {
+      visitor.visit(kind, level, errorCode, message);
+      visitor.visitVersion(version.getMajor(), version.getMinor(), version.getPatch());
+      visitor.visitEnd();
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/SanityCheck.java b/src/test/java/com/android/tools/r8/SanityCheck.java
index 38fa873..b82995c 100644
--- a/src/test/java/com/android/tools/r8/SanityCheck.java
+++ b/src/test/java/com/android/tools/r8/SanityCheck.java
@@ -29,7 +29,7 @@
 
   private static final String SRV_PREFIX = "META-INF/services/";
   private static final String METADATA_EXTENSION =
-      "com.android.tools.r8.jetbrains.kotlinx.metadata.internal.extensions.MetadataExtensions";
+      "com.android.tools.r8.jetbrains.kotlinx.metadata.impl.extensions.MetadataExtensions";
   private static final String EXT_IN_SRV = SRV_PREFIX + METADATA_EXTENSION;
 
     private void checkJarContent(
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
index 4f334aa..11ad201 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -94,7 +94,7 @@
             .compile()
             .inspect(
                 inspector ->
-                    assertEqualMetadataWithStringPoolValidation(
+                    assertEqualMetadata(
                         new CodeInspector(BASE_LIBRARY),
                         inspector,
                         (addedStrings, addedNonInitStrings) -> {}))
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 07539ed..55a048e 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
@@ -15,7 +15,6 @@
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestCompileResult;
-import com.android.tools.r8.kotlin.KotlinMetadataAnnotationWrapper;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.IntBox;
@@ -32,8 +31,8 @@
 import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
 import junit.framework.TestCase;
+import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
-import org.junit.Assert;
 
 public abstract class KotlinMetadataTestBase extends KotlinTestBase {
 
@@ -55,7 +54,7 @@
   static final String KT_FUNCTION1 = "Lkotlin/Function1;";
   static final String KT_COMPARABLE = "Lkotlin/Comparable;";
 
-  public void assertEqualMetadataWithStringPoolValidation(
+  public void assertEqualMetadata(
       CodeInspector originalInspector,
       CodeInspector rewrittenInspector,
       BiConsumer<Integer, Integer> addedStringsInspector) {
@@ -74,11 +73,9 @@
         continue;
       }
       assertNotNull(rewrittenMetadata);
-      KotlinMetadataAnnotationWrapper originalHeader =
-          KotlinMetadataAnnotationWrapper.wrap(originalMetadata);
-      KotlinMetadataAnnotationWrapper rewrittenHeader =
-          KotlinMetadataAnnotationWrapper.wrap(rewrittenMetadata);
-      TestCase.assertEquals(originalHeader.kind(), rewrittenHeader.kind());
+      KotlinClassHeader originalHeader = originalMetadata.getHeader();
+      KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+      TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
 
       // We cannot assert equality of the data since it may be ordered differently. However, we
       // will check for the changes to the string pool and then validate the same parsing
@@ -90,8 +87,8 @@
                   .computeIfAbsent(
                       method.getFinalSignature().toDescriptor(), ignoreArgument(ArrayList::new))
                   .add(method.getFinalName()));
-      HashSet<String> originalStrings = new HashSet<>(Arrays.asList(originalHeader.data2()));
-      HashSet<String> rewrittenStrings = new HashSet<>(Arrays.asList(rewrittenHeader.data2()));
+      HashSet<String> originalStrings = new HashSet<>(Arrays.asList(originalHeader.getData2()));
+      HashSet<String> rewrittenStrings = new HashSet<>(Arrays.asList(rewrittenHeader.getData2()));
       rewrittenStrings.forEach(
           rewrittenString -> {
             if (originalStrings.contains(rewrittenString)) {
@@ -111,7 +108,7 @@
             }
             addedNonInitStrings.increment();
           });
-      assertEquals(originalHeader.packageName(), rewrittenHeader.packageName());
+      assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
 
       String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
       String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
@@ -120,57 +117,6 @@
     addedStringsInspector.accept(addedStrings.get(), addedNonInitStrings.get());
   }
 
-  public void assertEqualDeserializedMetadata(
-      CodeInspector inspector, CodeInspector otherInspector) {
-    for (FoundClassSubject clazzSubject : otherInspector.allClasses()) {
-      ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
-      assertThat(r8Clazz, isPresent());
-      KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
-      KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
-      if (originalMetadata == null) {
-        assertNull(rewrittenMetadata);
-        continue;
-      }
-      assertNotNull(rewrittenMetadata);
-      KotlinMetadataAnnotationWrapper originalHeader =
-          KotlinMetadataAnnotationWrapper.wrap(originalMetadata);
-      KotlinMetadataAnnotationWrapper rewrittenHeader =
-          KotlinMetadataAnnotationWrapper.wrap(rewrittenMetadata);
-      TestCase.assertEquals(originalHeader.kind(), rewrittenHeader.kind());
-      TestCase.assertEquals(originalHeader.packageName(), rewrittenHeader.packageName());
-      // We cannot assert equality of the data since it may be ordered differently. We use the
-      // KotlinMetadataWriter to deserialize the metadata and assert those are equal.
-      String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
-      String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
-      TestCase.assertEquals(expected, actual);
-    }
-  }
-
-  public void assertEqualMetadata(CodeInspector inspector, CodeInspector otherInspector) {
-    for (FoundClassSubject clazzSubject : otherInspector.allClasses()) {
-      ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
-      assertThat(r8Clazz, isPresent());
-      KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
-      KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
-      if (originalMetadata == null) {
-        assertNull(rewrittenMetadata);
-        continue;
-      }
-      TestCase.assertNotNull(rewrittenMetadata);
-      KotlinMetadataAnnotationWrapper originalHeader =
-          KotlinMetadataAnnotationWrapper.wrap(originalMetadata);
-      KotlinMetadataAnnotationWrapper rewrittenHeader =
-          KotlinMetadataAnnotationWrapper.wrap(rewrittenMetadata);
-      TestCase.assertEquals(originalHeader.kind(), rewrittenHeader.kind());
-      TestCase.assertEquals(originalHeader.packageName(), rewrittenHeader.packageName());
-      Assert.assertArrayEquals(originalHeader.data1(), rewrittenHeader.data1());
-      Assert.assertArrayEquals(originalHeader.data2(), rewrittenHeader.data2());
-      String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
-      String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
-      TestCase.assertEquals(expected, actual);
-    }
-  }
-
   public static void verifyExpectedWarningsFromKotlinReflectAndStdLib(
       TestCompileResult<?, ?> compileResult) {
     compileResult.assertAllWarningMessagesMatch(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
index bcbc386..5e5fc28 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
@@ -4,13 +4,26 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
+
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.concurrent.ExecutionException;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -84,13 +97,7 @@
                 ProguardKeepAttributes.INNER_CLASSES,
                 ProguardKeepAttributes.ENCLOSING_METHOD)
             .compile()
-            // Since this has a keep-all classes rule, we should just assert that the meta-data is
-            // equal to the original one.
-            .inspect(
-                inspector ->
-                    assertEqualDeserializedMetadata(
-                        inspector,
-                        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion))))
+            .inspect(this::inspect)
             .writeToZip();
     Path main =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
@@ -105,6 +112,33 @@
         .assertSuccessWithOutput(EXPECTED);
   }
 
+  private void inspect(CodeInspector inspector) throws IOException, ExecutionException {
+    // Since this has a keep-all classes rule, we should just assert that the meta-data is equal to
+    // the original one.
+    CodeInspector stdLibInspector =
+        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion));
+    for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) {
+      ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+      assertThat(r8Clazz, isPresent());
+      KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+      KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+      if (originalMetadata == null) {
+        assertNull(rewrittenMetadata);
+        continue;
+      }
+      assertNotNull(rewrittenMetadata);
+      KotlinClassHeader originalHeader = originalMetadata.getHeader();
+      KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+      assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+      assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+      // We cannot assert equality of the data since it may be ordered differently. Instead we use
+      // the KotlinMetadataWriter.
+      String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+      String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+      assertEquals(expected, actual);
+    }
+  }
+
   @Test
   public void testMetadataForReflect() throws Exception {
     Path libJar =
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassTest.java
index 0704ecd..af22ae1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlineClassTest.java
@@ -5,15 +5,24 @@
 package com.android.tools.r8.kotlin.metadata;
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_6_0;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
+import junit.framework.TestCase;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -75,11 +84,7 @@
                 "-keep class " + PKG + ".inline_class_lib.LibKt { *** login-*(java.lang.String); }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             .compile()
-            .inspect(
-                inspector ->
-                    assertEqualMetadata(
-                        inspector,
-                        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion))))
+            .inspect(this::inspect)
             .writeToZip();
     Path main =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
@@ -93,4 +98,24 @@
         .run(parameters.getRuntime(), PKG + ".inline_class_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
   }
+
+  private void inspect(CodeInspector inspector) throws IOException {
+    CodeInspector stdLibInspector =
+        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion));
+    ClassSubject clazzSubject = stdLibInspector.clazz(passwordTypeName);
+    ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+    assertThat(r8Clazz, isPresent());
+    KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+    KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+    TestCase.assertNotNull(rewrittenMetadata);
+    KotlinClassHeader originalHeader = originalMetadata.getHeader();
+    KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+    TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+    TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+    Assert.assertArrayEquals(originalHeader.getData1(), rewrittenHeader.getData1());
+    Assert.assertArrayEquals(originalHeader.getData2(), rewrittenHeader.getData2());
+    String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+    String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+    TestCase.assertEquals(expected, actual);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
index 6f78827..c693a41 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
@@ -4,13 +4,24 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
+
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
+import junit.framework.TestCase;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -67,11 +78,7 @@
                 ProguardKeepAttributes.INNER_CLASSES,
                 ProguardKeepAttributes.ENCLOSING_METHOD)
             .compile()
-            .inspect(
-                inspector ->
-                    assertEqualDeserializedMetadata(
-                        inspector,
-                        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion))))
+            .inspect(this::inspect)
             .writeToZip();
     Path main =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
@@ -85,4 +92,29 @@
         .run(parameters.getRuntime(), PKG + ".inline_property_app.MainKt")
         .assertSuccessWithOutput(EXPECTED);
   }
+
+  private void inspect(CodeInspector inspector) throws IOException {
+    CodeInspector stdLibInspector =
+        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion));
+    for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) {
+      ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+      assertThat(r8Clazz, isPresent());
+      KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+      KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+      if (originalMetadata == null) {
+        assertNull(rewrittenMetadata);
+        continue;
+      }
+      TestCase.assertNotNull(rewrittenMetadata);
+      KotlinClassHeader originalHeader = originalMetadata.getHeader();
+      KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+      TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+      TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+      // We cannot assert equality of the data since it may be ordered differently. Instead we use
+      // the KotlinMetadataWriter.
+      String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+      String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+      TestCase.assertEquals(expected, actual);
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
index ce9fba4..24b3e58 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
@@ -10,12 +10,10 @@
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.kotlin.KotlinMetadataAnnotationWrapper;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import java.util.Collection;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -70,9 +68,8 @@
       if (clazz.getFinalName().startsWith("kotlin.io")
           || clazz.getFinalName().equals("kotlin.Metadata")
           || clazz.getFinalName().equals("kotlin.jvm.JvmName")) {
-        KotlinClassMetadata kotlinClassMetadata = clazz.getKotlinClassMetadata();
-        assertNotNull(kotlinClassMetadata);
-        assertNotNull(KotlinMetadataAnnotationWrapper.wrap(kotlinClassMetadata).data2());
+        assertNotNull(clazz.getKotlinClassMetadata());
+        assertNotNull(clazz.getKotlinClassMetadata().getHeader().getData2());
       } else {
         assertNull(clazz.getKotlinClassMetadata());
       }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java
index 9fddbf0..2a7ee54 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteLocalDelegatedPropertyTest.java
@@ -70,7 +70,7 @@
             .compile()
             .inspect(
                 inspector ->
-                    assertEqualMetadataWithStringPoolValidation(
+                    assertEqualMetadata(
                         new CodeInspector(jars.getForConfiguration(kotlinc, targetVersion)),
                         inspector,
                         (addedStrings, addedNonInitStrings) -> {}))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
index e06e1ba..d08e0a0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -56,7 +56,7 @@
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .inspect(
             inspector ->
-                assertEqualMetadataWithStringPoolValidation(
+                assertEqualMetadata(
                     new CodeInspector(kotlinc.getKotlinStdlibJar()),
                     inspector,
                     (addedStrings, addedNonInitStrings) -> {
@@ -74,7 +74,7 @@
         .compile()
         .inspect(
             inspector ->
-                assertEqualMetadataWithStringPoolValidation(
+                assertEqualMetadata(
                     new CodeInspector(kotlinc.getKotlinStdlibJar()),
                     inspector,
                     (addedStrings, addedNonInitStrings) -> {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRawTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRawTest.java
index 6012fb0..7686ac3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRawTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRawTest.java
@@ -96,7 +96,7 @@
             .compile()
             .inspect(
                 inspector ->
-                    assertEqualMetadataWithStringPoolValidation(
+                    assertEqualMetadata(
                         new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion)),
                         inspector,
                         (addedStrings, addedNonInitStrings) -> {}))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
index 4683d0b..5d13395 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteUnitPrimitiveTest.java
@@ -6,16 +6,27 @@
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
 import static com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion.JAVA_8;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
 
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
+import junit.framework.TestCase;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -98,12 +109,7 @@
             .compile()
             .assertAllWarningMessagesMatch(
                 equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
-            .inspect(
-                inspector ->
-                    assertEqualMetadata(
-                        inspector,
-                        new CodeInspector(
-                            kotlincLibJar.getForConfiguration(kotlinc, targetVersion))))
+            .inspect(this::inspect)
             .writeToZip();
     Path main =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
@@ -118,4 +124,29 @@
         .run(parameters.getRuntime(), PKG_APP + ".MainKt")
         .assertSuccessWithOutput(EXPECTED);
   }
+
+  private void inspect(CodeInspector inspector) throws IOException {
+    CodeInspector stdLibInspector =
+        new CodeInspector(kotlincLibJar.getForConfiguration(kotlinc, targetVersion));
+    for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) {
+      ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+      assertThat(r8Clazz, isPresent());
+      KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+      KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+      if (originalMetadata == null) {
+        assertNull(rewrittenMetadata);
+        continue;
+      }
+      TestCase.assertNotNull(rewrittenMetadata);
+      KotlinClassHeader originalHeader = originalMetadata.getHeader();
+      KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+      TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+      TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+      Assert.assertArrayEquals(originalHeader.getData1(), rewrittenHeader.getData1());
+      Assert.assertArrayEquals(originalHeader.getData2(), rewrittenHeader.getData2());
+      String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+      String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+      TestCase.assertEquals(expected, actual);
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
index b65643f..6aecc85 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
@@ -9,6 +9,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertNull;
 
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
@@ -19,9 +20,13 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
+import junit.framework.TestCase;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -107,13 +112,31 @@
   }
 
   private void inspect(CodeInspector inspector) throws IOException {
-    assertEqualDeserializedMetadata(
-        inspector, new CodeInspector(kotlincLibJar.getForConfiguration(kotlinc, targetVersion)));
-    ClassSubject r8Clazz = inspector.clazz(PKG_LIB + ".Name");
-    assertThat(r8Clazz, isPresent());
-    String actual =
-        KotlinMetadataWriter.kotlinMetadataToString("", r8Clazz.getKotlinClassMetadata());
-    assertThat(actual, containsString("inlineClassUnderlyingPropertyName"));
-    assertThat(actual, containsString("inlineClassUnderlyingType"));
+    CodeInspector stdLibInspector =
+        new CodeInspector(kotlincLibJar.getForConfiguration(kotlinc, targetVersion));
+    for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) {
+      ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+      assertThat(r8Clazz, isPresent());
+      KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+      KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+      if (originalMetadata == null) {
+        assertNull(rewrittenMetadata);
+        continue;
+      }
+      TestCase.assertNotNull(rewrittenMetadata);
+      KotlinClassHeader originalHeader = originalMetadata.getHeader();
+      KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+      TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+      TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+      // We cannot assert equality of the data since it may be ordered differently. Instead we use
+      // the KotlinMetadataWriter.
+      String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+      String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+      TestCase.assertEquals(expected, actual);
+      if (r8Clazz.getFinalName().equals(PKG_LIB + ".Name")) {
+        assertThat(actual, containsString("inlineClassUnderlyingPropertyName"));
+        assertThat(actual, containsString("inlineClassUnderlyingType"));
+      }
+    }
   }
 }