Merge commit '1240b3d2d5a9a2349305ac803c954ebcaad549f4' into dev-release
diff --git a/build.gradle b/build.gradle
index a3021ec..870b615 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.6.0'
-    kotlinExtMetadataJVMVersion = '0.5.0'
+    kotlinVersion = '1.8.0'
+    kotlinExtMetadataJVMVersion = '0.6.0'
     smaliVersion = '2.2b4'
     errorproneVersion = '2.3.2'
     testngVersion = '6.10'
@@ -286,7 +286,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
deleted file mode 100644
index 65bdeec..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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 249f68f..759ef69 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
@@ -14,6 +14,7 @@
 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;
 
@@ -49,8 +50,7 @@
     return builder.build();
   }
 
-  boolean rewrite(
-      KmVisitorProviders.KmAnnotationVisitorProvider visitorProvider, AppView<?> appView) {
+  boolean rewrite(Consumer<KmAnnotation> annotationConsumer, AppView<?> appView) {
     BooleanBox rewritten = new BooleanBox(false);
     rewritten.or(
         annotationType.toRenamedDescriptorOrDefault(
@@ -72,7 +72,7 @@
                                 }
                               },
                               appView)));
-              visitorProvider.get(new KmAnnotation(classifier, rewrittenArguments));
+              annotationConsumer.accept(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 810f2de..d59a9ca 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -4,9 +4,12 @@
 
 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;
@@ -17,6 +20,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.Reporter;
 import com.google.common.collect.ImmutableList;
@@ -26,13 +30,12 @@
 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 {
@@ -57,6 +60,8 @@
   private final KotlinTypeInfo inlineClassUnderlyingType;
   private final int jvmFlags;
   private final String companionObjectName;
+  // Collection of context receiver types
+  private final List<KotlinTypeInfo> contextReceiverTypes;
 
   // List of tracked assignments of kotlin metadata.
   private final KotlinMetadataMembersTracker originalMembersWithKotlinInfo;
@@ -82,7 +87,8 @@
       KotlinTypeInfo inlineClassUnderlyingType,
       KotlinMetadataMembersTracker originalMembersWithKotlinInfo,
       int jvmFlags,
-      String companionObjectName) {
+      String companionObjectName,
+      List<KotlinTypeInfo> contextReceiverTypes) {
     this.flags = flags;
     this.name = name;
     this.nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin =
@@ -105,6 +111,7 @@
     this.originalMembersWithKotlinInfo = originalMembersWithKotlinInfo;
     this.jvmFlags = jvmFlags;
     this.companionObjectName = companionObjectName;
+    this.contextReceiverTypes = contextReceiverTypes;
   }
 
   public static KotlinClassInfo create(
@@ -188,7 +195,10 @@
         KotlinTypeInfo.create(kmClass.getInlineClassUnderlyingType(), factory, reporter),
         originalMembersWithKotlinInfo,
         JvmExtensionsKt.getJvmFlags(kmClass),
-        setCompanionObject(kmClass, hostClass, reporter));
+        setCompanionObject(kmClass, hostClass, reporter),
+        ListUtils.map(
+            kmClass.getContextReceiverTypes(),
+            contextRecieverType -> KotlinTypeInfo.create(contextRecieverType, factory, reporter)));
   }
 
   private static KotlinTypeReference getAnonymousObjectOrigin(
@@ -279,7 +289,7 @@
   }
 
   @Override
-  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
     KmClass kmClass = new KmClass();
     // TODO(b/154348683): Set flags.
     kmClass.setFlags(flags);
@@ -356,27 +366,29 @@
     // Rewrite functions, type-aliases and type-parameters.
     rewritten |=
         declarationContainerInfo.rewrite(
-            kmClass::visitFunction,
-            kmClass::visitProperty,
-            kmClass::visitTypeAlias,
+            kmClass.getFunctions()::add,
+            kmClass.getProperties()::add,
+            kmClass.getTypeAliases()::add,
             clazz,
             appView,
             rewrittenReferences);
     // Rewrite type parameters.
-    for (KotlinTypeParameterInfo typeParameter : typeParameters) {
-      rewritten |= typeParameter.rewrite(kmClass::visitTypeParameter, appView);
-    }
+    rewritten |=
+        rewriteList(
+            appView, typeParameters, kmClass.getTypeParameters(), KotlinTypeParameterInfo::rewrite);
     // 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(kmClass::visitSupertype, appView);
+        rewritten |= superType.rewrite(rewrittenSuperTypes::add, appView);
       } else {
         rewritten = true;
       }
     }
     // Rewrite nested classes.
-    for (KotlinTypeReference nestedClass : nestedClasses) {
+    List<String> rewrittenNestedClasses = kmClass.getNestedClasses();
+    for (KotlinTypeReference nestedClass : this.nestedClasses) {
       Box<String> nestedDescriptorBox = new Box<>();
       boolean nestedClassRewritten =
           nestedClass.toRenamedBinaryNameOrDefault(nestedDescriptorBox::set, appView, null);
@@ -386,20 +398,21 @@
           // is the name we should record.
           String nestedDescriptor = nestedDescriptorBox.get();
           int innerClassIndex = nestedDescriptor.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
-          kmClass.visitNestedClass(nestedDescriptor.substring(innerClassIndex + 1));
+          rewrittenNestedClasses.add(nestedDescriptor.substring(innerClassIndex + 1));
         } else {
-          kmClass.visitNestedClass(nestedClass.getOriginalName());
+          rewrittenNestedClasses.add(nestedClass.getOriginalName());
         }
       }
       rewritten |= nestedClassRewritten;
     }
-    // Rewrite sealed sub classes.
+    // Rewrite sealed sub-classes.
+    List<String> rewrittenSealedClasses = kmClass.getSealedSubclasses();
     for (KotlinTypeReference sealedSubClass : sealedSubClasses) {
       rewritten |=
           sealedSubClass.toRenamedBinaryNameOrDefault(
               sealedName -> {
                 if (sealedName != null) {
-                  kmClass.visitSealedSubclass(
+                  rewrittenSealedClasses.add(
                       sealedName.replace(
                           DescriptorUtils.INNER_CLASS_SEPARATOR,
                           DescriptorUtils.JAVA_PACKAGE_SEPARATOR));
@@ -408,34 +421,36 @@
               appView,
               null);
     }
-    rewritten |= versionRequirements.rewrite(kmClass::visitVersionRequirement);
+    rewritten |= versionRequirements.rewrite(kmClass.getVersionRequirements()::addAll);
     if (inlineClassUnderlyingPropertyName != null && inlineClassUnderlyingType != null) {
       kmClass.setInlineClassUnderlyingPropertyName(inlineClassUnderlyingPropertyName);
       rewritten |=
-          inlineClassUnderlyingType.rewrite(kmClass::visitInlineClassUnderlyingType, appView);
+          inlineClassUnderlyingType.rewrite(kmClass::setInlineClassUnderlyingType, appView);
     }
-    JvmClassExtensionVisitor extensionVisitor =
-        (JvmClassExtensionVisitor) kmClass.visitExtensions(JvmClassExtensionVisitor.TYPE);
-    extensionVisitor.visitJvmFlags(jvmFlags);
-    extensionVisitor.visitModuleName(moduleName);
+    rewritten |=
+        rewriteList(
+            appView,
+            contextReceiverTypes,
+            kmClass.getContextReceiverTypes(),
+            KotlinTypeInfo::rewrite);
+    JvmExtensionsKt.setJvmFlags(kmClass, jvmFlags);
+    JvmExtensionsKt.setModuleName(kmClass, moduleName);
     if (anonymousObjectOrigin != null) {
       rewritten |=
           anonymousObjectOrigin.toRenamedBinaryNameOrDefault(
               renamedAnon -> {
                 if (renamedAnon != null) {
-                  extensionVisitor.visitAnonymousObjectOriginName(renamedAnon);
+                  JvmExtensionsKt.setAnonymousObjectOriginName(kmClass, renamedAnon);
                 }
               },
               appView,
               null);
     }
     rewritten |=
-        localDelegatedProperties.rewrite(extensionVisitor::visitLocalDelegatedProperty, appView);
-    extensionVisitor.visitEnd();
-    KotlinClassMetadata.Class.Writer writer = new KotlinClassMetadata.Class.Writer();
-    kmClass.accept(writer);
+        localDelegatedProperties.rewrite(
+            JvmExtensionsKt.getLocalDelegatedProperties(kmClass)::add, appView);
     return Pair.create(
-        writer.write().getHeader(),
+        Companion.writeClass(kmClass, getCompatibleKotlinInfo(), 0).getAnnotationData(),
         rewritten || !originalMembersWithKotlinInfo.isEqual(rewrittenReferences, appView));
   }
 
@@ -457,6 +472,7 @@
     forEachApply(superTypes, type -> type::trace, definitionSupplier);
     forEachApply(sealedSubClasses, sealedClass -> sealedClass::trace, definitionSupplier);
     forEachApply(nestedClasses, nested -> nested::trace, definitionSupplier);
+    forEachApply(contextReceiverTypes, nested -> nested::trace, definitionSupplier);
     localDelegatedProperties.trace(definitionSupplier);
     // TODO(b/154347404): trace enum entries.
     if (anonymousObjectOrigin != null) {
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 e8f7fd0..d634e88 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 kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlin.Metadata;
 
 public interface KotlinClassLevelInfo extends EnqueuerMetadataTraceable {
 
@@ -56,7 +56,7 @@
     return null;
   }
 
-  Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView);
+  Pair<Metadata, 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 eb07f4b..7bae38e 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 {
-      KotlinClassHeader header = new KotlinClassHeader(k, mv, d1, d2, xs, pn, xi);
-      return KotlinClassMetadata.read(header);
+      return KotlinClassMetadata.read(
+          new KotlinMetadataAnnotationWrapper(k, mv, d1, d2, xs, pn, xi));
     } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
       throw new KotlinMetadataException(e);
     }
@@ -142,8 +142,9 @@
       KotlinClassMetadata kMetadata,
       AppView<?> appView,
       Consumer<DexEncodedMethod> keepByteCode) {
-    String packageName = kMetadata.getHeader().getPackageName();
-    int[] metadataVersion = kMetadata.getHeader().getMetadataVersion();
+    Metadata annotationData = kMetadata.getAnnotationData();
+    String packageName = annotationData.pn();
+    int[] metadataVersion = annotationData.mv();
     if (kMetadata instanceof KotlinClassMetadata.Class) {
       return KotlinClassInfo.create(
           (KotlinClassMetadata.Class) kMetadata,
@@ -178,7 +179,7 @@
           kotlin,
           appView);
     } else {
-      throw new MetadataError("unsupported 'k' value: " + kMetadata.getHeader().getKind());
+      throw new MetadataError("unsupported 'k' value: " + annotationData.k());
     }
   }
 
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 4c93733..c8dac29 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -4,6 +4,7 @@
 
 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;
@@ -67,10 +68,13 @@
               method,
               appView);
     }
-    for (KotlinValueParameterInfo valueParameterInfo : valueParameters) {
-      rewritten |= valueParameterInfo.rewrite(kmConstructor::visitValueParameter, appView);
-    }
-    rewritten |= versionRequirements.rewrite(kmConstructor::visitVersionRequirement);
+    rewritten |=
+        rewriteList(
+            appView,
+            valueParameters,
+            kmConstructor.getValueParameters(),
+            KotlinValueParameterInfo::rewrite);
+    rewritten |= versionRequirements.rewrite(kmConstructor.getVersionRequirements()::addAll);
     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 c437bba..57466ae 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinContractInfo.java
@@ -4,6 +4,8 @@
 
 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;
@@ -13,8 +15,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 {
@@ -48,17 +50,11 @@
     forEachApply(effects, effect -> effect::trace, definitionSupplier);
   }
 
-  boolean rewrite(
-      KmVisitorProviders.KmContractVisitorProvider visitorProvider, AppView<?> appView) {
+  boolean rewrite(Consumer<KmContract> consumer, AppView<?> appView) {
     if (this == NO_EFFECT) {
       return false;
     }
-    boolean rewritten = false;
-    KmContractVisitor kmContractVisitor = visitorProvider.get();
-    for (KotlinEffectInfo effect : effects) {
-      rewritten |= effect.rewrite(kmContractVisitor::visitEffect, appView);
-    }
-    kmContractVisitor.visitEnd();
-    return rewritten;
+    KmContract kmContract = consume(new KmContract(), consumer);
+    return rewriteList(appView, effects, kmContract.getEffects(), KotlinEffectInfo::rewrite);
   }
 }
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 9307053..9abed29 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -5,6 +5,7 @@
 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;
 
@@ -179,17 +180,15 @@
   }
 
   boolean rewrite(
-      KmVisitorProviders.KmFunctionVisitorProvider functionProvider,
-      KmVisitorProviders.KmPropertyVisitorProvider propertyProvider,
-      KmVisitorProviders.KmTypeAliasVisitorProvider typeAliasProvider,
+      Consumer<KmFunction> functionConsumer,
+      Consumer<KmProperty> propertyConsumer,
+      Consumer<KmTypeAlias> typeAliasConsumer,
       DexClass clazz,
       AppView<?> appView,
       KotlinMetadataMembersTracker rewrittenMembersWithKotlinInfo) {
     // Type aliases only have a representation here, so we can generate them directly.
-    boolean rewritten = false;
-    for (KotlinTypeAliasInfo typeAlias : typeAliases) {
-      rewritten |= typeAlias.rewrite(typeAliasProvider, appView);
-    }
+    boolean rewritten =
+        rewriteList(appView, typeAliases, typeAliasConsumer, KotlinTypeAliasInfo::rewrite);
     // For properties, we need to combine potentially a field, setter and getter.
     Map<KotlinPropertyInfo, KotlinPropertyGroup> properties = new LinkedHashMap<>();
     for (DexEncodedField field : clazz.fields()) {
@@ -203,7 +202,7 @@
     }
     for (DexEncodedMethod method : clazz.methods()) {
       if (method.getKotlinInfo().isFunction()) {
-        rewritten |= method.getKotlinInfo().asFunction().rewrite(functionProvider, method, appView);
+        rewritten |= method.getKotlinInfo().asFunction().rewrite(functionConsumer, method, appView);
         rewrittenMembersWithKotlinInfo.add(method.getReference());
         continue;
       }
@@ -225,19 +224,25 @@
       KotlinPropertyGroup kotlinPropertyGroup = properties.get(kotlinPropertyInfo);
       rewritten |=
           kotlinPropertyInfo.rewrite(
-              propertyProvider,
+              propertyConsumer,
               kotlinPropertyGroup.backingField,
               kotlinPropertyGroup.getter,
               kotlinPropertyGroup.setter,
               appView);
     }
     // Add all not backed functions and properties.
-    for (KotlinFunctionInfo notBackedFunction : functionsWithNoBacking) {
-      rewritten |= notBackedFunction.rewrite(functionProvider, null, appView);
-    }
-    for (KotlinPropertyInfo notBackedProperty : propertiesWithNoBacking) {
-      rewritten |= notBackedProperty.rewrite(propertyProvider, null, null, null, appView);
-    }
+    rewritten |=
+        rewriteList(
+            appView,
+            functionsWithNoBacking,
+            functionConsumer,
+            KotlinFunctionInfo::rewriteNoBacking);
+    rewritten |=
+        rewriteList(
+            appView,
+            propertiesWithNoBacking,
+            propertyConsumer,
+            KotlinPropertyInfo::rewriteNoBacking);
     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 18afe26..4268f32 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectExpressionInfo.java
@@ -4,19 +4,21 @@
 
 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 {
 
@@ -84,26 +86,31 @@
     forEachApply(orArguments, arg -> arg::trace, definitionSupplier);
   }
 
-  boolean rewrite(KmEffectExpressionVisitorProvider provider, AppView<?> appView) {
+  boolean rewrite(Consumer<KmEffectExpression> consumer, AppView<?> appView) {
     if (this == NO_EXPRESSION) {
       return false;
     }
-    KmEffectExpressionVisitor visitor = provider.get();
-    visitor.visit(flags, parameterIndex);
+    KmEffectExpression effectExpression = consume(new KmEffectExpression(), consumer);
+    effectExpression.setFlags(flags);
+    effectExpression.setParameterIndex(parameterIndex);
     if (constantValue != null) {
-      visitor.visitConstantValue(constantValue.getValue());
+      effectExpression.setConstantValue(constantValue);
     }
-    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();
+    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);
     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 e93b126..4f38113 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinEffectInfo.java
@@ -4,19 +4,20 @@
 
 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 {
 
@@ -50,14 +51,15 @@
     conclusion.trace(definitionSupplier);
   }
 
-  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();
+  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);
     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 3daa33d..59e482f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -4,15 +4,17 @@
 
 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
@@ -57,12 +59,12 @@
   }
 
   @Override
-  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
     KmPackage kmPackage = new KmPackage();
     boolean rewritten = packageInfo.rewrite(kmPackage, clazz, appView);
-    KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
-    kmPackage.accept(writer);
-    return Pair.create(writer.write().getHeader(), rewritten);
+    return Pair.create(
+        Companion.writeFileFacade(kmPackage, getCompatibleKotlinInfo(), 0).getAnnotationData(),
+        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 35e52f5..1edbe51 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFlexibleTypeUpperBoundInfo.java
@@ -9,6 +9,7 @@
 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 {
@@ -37,8 +38,7 @@
         KotlinTypeInfo.create(flexibleTypeUpperBound.getType(), factory, reporter));
   }
 
-  boolean rewrite(
-      KmVisitorProviders.KmFlexibleUpperBoundVisitorProvider visitorProvider, AppView<?> appView) {
+  boolean rewrite(Consumer<KmFlexibleTypeUpperBound> consumer, AppView<?> appView) {
     if (this == NO_FLEXIBLE_UPPER_BOUND) {
       // Nothing to do.
       return false;
@@ -47,7 +47,9 @@
       assert false;
       return false;
     }
-    return kotlinTypeInfo.rewrite(flags -> visitorProvider.get(flags, typeFlexibilityId), appView);
+    return kotlinTypeInfo.rewrite(
+        kmType -> consumer.accept(new KmFlexibleTypeUpperBound(kmType, 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 f5b40c2..351acc9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -4,18 +4,21 @@
 
 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.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+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 {
@@ -41,6 +44,8 @@
   private final KotlinContractInfo contract;
   // A value describing if any of the parameters are crossinline.
   private final boolean crossInlineParameter;
+  // Collection of context receiver types
+  private final List<KotlinTypeInfo> contextReceiverTypes;
 
   private KotlinFunctionInfo(
       int flags,
@@ -53,7 +58,8 @@
       KotlinTypeReference lambdaClassOrigin,
       KotlinVersionRequirementInfo versionRequirements,
       KotlinContractInfo contract,
-      boolean crossInlineParameter) {
+      boolean crossInlineParameter,
+      List<KotlinTypeInfo> contextReceiverTypes) {
     this.flags = flags;
     this.name = name;
     this.returnType = returnType;
@@ -65,6 +71,7 @@
     this.versionRequirements = versionRequirements;
     this.contract = contract;
     this.crossInlineParameter = crossInlineParameter;
+    this.contextReceiverTypes = contextReceiverTypes;
   }
 
   public boolean hasCrossInlineParameter() {
@@ -98,7 +105,10 @@
         getlambdaClassOrigin(kmFunction, factory),
         KotlinVersionRequirementInfo.create(kmFunction.getVersionRequirements()),
         KotlinContractInfo.create(kmFunction.getContract(), factory, reporter),
-        isCrossInline);
+        isCrossInline,
+        ListUtils.map(
+            kmFunction.getContextReceiverTypes(),
+            contextRecieverType -> KotlinTypeInfo.create(contextRecieverType, factory, reporter)));
   }
 
   private static KotlinTypeReference getlambdaClassOrigin(
@@ -115,10 +125,11 @@
     return name;
   }
 
-  boolean rewrite(
-      KmVisitorProviders.KmFunctionVisitorProvider visitorProvider,
-      DexEncodedMethod method,
-      AppView<?> appView) {
+  boolean rewriteNoBacking(Consumer<KmFunction> consumer, AppView<?> appView) {
+    return rewrite(consumer, null, appView);
+  }
+
+  boolean rewrite(Consumer<KmFunction> consumer, DexEncodedMethod method, AppView<?> appView) {
     // TODO(b/154348683): Check method for flags to pass in.
     boolean rewritten = false;
     String finalName = name;
@@ -132,36 +143,51 @@
         finalName = rewrittenName;
       }
     }
-    KmFunctionVisitor kmFunction = visitorProvider.get(flags, finalName);
+    KmFunction kmFunction = consume(new KmFunction(flags, finalName), consumer);
     // TODO(b/154348149): ReturnType could have been merged to a subtype.
-    rewritten |= returnType.rewrite(kmFunction::visitReturnType, appView);
-    for (KotlinValueParameterInfo valueParameterInfo : valueParameters) {
-      rewritten |= valueParameterInfo.rewrite(kmFunction::visitValueParameter, appView);
+    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);
     }
-    for (KotlinTypeParameterInfo typeParameterInfo : typeParameters) {
-      rewritten |= typeParameterInfo.rewrite(kmFunction::visitTypeParameter, 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) {
+    if (lambdaClassOrigin != null) {
       rewritten |=
           lambdaClassOrigin.toRenamedBinaryNameOrDefault(
               lambdaClassOriginName -> {
                 if (lambdaClassOriginName != null) {
-                  extensionVisitor.visitLambdaClassOriginName(lambdaClassOriginName);
+                  JvmExtensionsKt.setLambdaClassOriginName(kmFunction, lambdaClassOriginName);
                 }
               },
               appView,
               null);
     }
-    rewritten |= contract.rewrite(kmFunction::visitContract, appView);
+    rewritten |= contract.rewrite(kmFunction::setContract, appView);
     return rewritten;
   }
 
@@ -187,6 +213,7 @@
       receiverParameterType.trace(definitionSupplier);
     }
     forEachApply(typeParameters, param -> param::trace, definitionSupplier);
+    forEachApply(contextReceiverTypes, type -> type::trace, definitionSupplier);
     if (signature != null) {
       signature.trace(definitionSupplier);
     }
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 2760ece..2c5e817 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -70,6 +70,10 @@
     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 ffe08b4..9900e81 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -4,6 +4,7 @@
 
 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;
@@ -13,6 +14,7 @@
 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;
@@ -56,12 +58,10 @@
     return new KotlinLambdaInfo(kotlinFunctionInfo, false);
   }
 
-  boolean rewrite(
-      KmVisitorProviders.KmLambdaVisitorProvider visitorProvider,
-      DexClass clazz,
-      AppView<?> appView) {
+  boolean rewrite(Consumer<KmLambda> consumer, DexClass clazz, AppView<?> appView) {
+    KmLambda kmLambda = consume(new KmLambda(), consumer);
     if (!hasBacking) {
-      function.rewrite(visitorProvider.get()::visitFunction, null, appView);
+      function.rewrite(kmLambda::setFunction, null, appView);
       return true;
     }
     DexEncodedMethod backing = null;
@@ -71,7 +71,7 @@
         break;
       }
     }
-    return function.rewrite(visitorProvider.get()::visitFunction, backing, appView);
+    return function.rewrite(kmLambda::setFunction, 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 4bdce09..9fb8b43 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLocalDelegatedPropertyInfo.java
@@ -4,16 +4,17 @@
 
 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 {
@@ -51,11 +52,7 @@
     forEachApply(propertyInfos, prop -> prop::trace, definitionSupplier);
   }
 
-  boolean rewrite(KmPropertyVisitorProvider visitorProvider, AppView<?> appView) {
-    boolean rewritten = false;
-    for (KotlinPropertyInfo propertyInfo : propertyInfos) {
-      rewritten |= propertyInfo.rewrite(visitorProvider, null, null, null, appView);
-    }
-    return rewritten;
+  boolean rewrite(Consumer<KmProperty> consumer, AppView<?> appView) {
+    return rewriteList(appView, propertyInfos, consumer, KotlinPropertyInfo::rewriteNoBacking);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java
new file mode 100644
index 0000000..efbe154
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java
@@ -0,0 +1,123 @@
+// 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 c06aea8..e57fc63 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -27,7 +27,6 @@
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import kotlinx.metadata.jvm.KotlinClassHeader;
 
 public class KotlinMetadataRewriter {
 
@@ -155,17 +154,16 @@
       DexAnnotation oldMeta,
       WriteMetadataFieldInfo writeMetadataFieldInfo) {
     try {
-      Pair<KotlinClassHeader, Boolean> kotlinClassHeader = kotlinInfo.rewrite(clazz, appView);
+      Pair<kotlin.Metadata, Boolean> kotlinMetadata = kotlinInfo.rewrite(clazz, appView);
       // TODO(b/185756596): Remove when special handling is no longer needed.
-      if (!kotlinClassHeader.getSecond()
-          && appView.options().testing.keepMetadataInR8IfNotRewritten) {
+      if (!kotlinMetadata.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(
-                        kotlinClassHeader.getFirst(),
+                        kotlinMetadata.getFirst(),
                         kotlinInfo.getPackageName(),
                         getMaxVersion(METADATA_VERSION_1_4, kotlinInfo.getMetadataVersion()),
                         writeMetadataFieldInfo)));
@@ -173,7 +171,7 @@
       }
       DexAnnotation newMeta =
           createKotlinMetadataAnnotation(
-              kotlinClassHeader.getFirst(),
+              kotlinMetadata.getFirst(),
               kotlinInfo.getPackageName(),
               getMaxVersion(METADATA_VERSION_1_4, kotlinInfo.getMetadataVersion()),
               writeMetadataFieldInfo);
@@ -222,7 +220,7 @@
   }
 
   private DexAnnotation createKotlinMetadataAnnotation(
-      KotlinClassHeader header,
+      kotlin.Metadata metadata,
       String packageName,
       int[] metadataVersion,
       WriteMetadataFieldInfo writeMetadataFieldInfo) {
@@ -234,31 +232,30 @@
     }
     if (writeMetadataFieldInfo.writeKind) {
       elements.add(
-          new DexAnnotationElement(kotlin.metadata.kind, DexValueInt.create(header.getKind())));
+          new DexAnnotationElement(kotlin.metadata.kind, DexValueInt.create(metadata.k())));
     }
     if (writeMetadataFieldInfo.writeData1) {
       elements.add(
-          new DexAnnotationElement(kotlin.metadata.data1, createStringArray(header.getData1())));
+          new DexAnnotationElement(kotlin.metadata.data1, createStringArray(metadata.d1())));
     }
     if (writeMetadataFieldInfo.writeData2) {
       elements.add(
-          new DexAnnotationElement(kotlin.metadata.data2, createStringArray(header.getData2())));
+          new DexAnnotationElement(kotlin.metadata.data2, createStringArray(metadata.d2())));
     }
     if (writeMetadataFieldInfo.writePackageName && packageName != null && !packageName.isEmpty()) {
       elements.add(
           new DexAnnotationElement(
               kotlin.metadata.packageName, new DexValueString(factory.createString(packageName))));
     }
-    if (writeMetadataFieldInfo.writeExtraString && !header.getExtraString().isEmpty()) {
+    if (writeMetadataFieldInfo.writeExtraString && !metadata.xs().isEmpty()) {
       elements.add(
           new DexAnnotationElement(
               kotlin.metadata.extraString,
-              new DexValueString(factory.createString(header.getExtraString()))));
+              new DexValueString(factory.createString(metadata.xs()))));
     }
-    if (writeMetadataFieldInfo.writeExtraInt && header.getExtraInt() != 0) {
+    if (writeMetadataFieldInfo.writeExtraInt && metadata.xi() != 0) {
       elements.add(
-          new DexAnnotationElement(
-              kotlin.metadata.extraInt, DexValueInt.create(header.getExtraInt())));
+          new DexAnnotationElement(kotlin.metadata.extraInt, DexValueInt.create(metadata.xi())));
     }
     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 d5884f5..67a900a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -20,7 +20,11 @@
 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;
@@ -28,7 +32,7 @@
 import kotlinx.metadata.jvm.JvmFieldSignature;
 import kotlinx.metadata.jvm.JvmMethodSignature;
 import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
-import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
 
 public class KotlinMetadataUtils {
 
@@ -50,7 +54,7 @@
     }
 
     @Override
-    public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+    public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
       throw new Unreachable("Should never be called");
     }
 
@@ -232,4 +236,42 @@
     }
     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/KotlinMetadataWriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
index 1e4d633..dfb5d76 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
@@ -379,20 +379,31 @@
         indent,
         "constructors",
         sb,
-        newIndent -> {
-          appendKmList(
-              newIndent,
-              "KmConstructor",
-              sb,
-              kmClass.getConstructors().stream()
-                  .sorted(
-                      Comparator.comparing(
-                          kmConstructor -> JvmExtensionsKt.getSignature(kmConstructor).asString()))
-                  .collect(Collectors.toList()),
-              (nextIndent, constructor) -> {
-                appendKmConstructor(nextIndent, sb, constructor);
-              });
-        });
+        newIndent ->
+            appendKmList(
+                newIndent,
+                "KmConstructor",
+                sb,
+                kmClass.getConstructors().stream()
+                    .sorted(
+                        Comparator.comparing(
+                            kmConstructor ->
+                                JvmExtensionsKt.getSignature(kmConstructor).asString()))
+                    .collect(Collectors.toList()),
+                (nextIndent, constructor) -> {
+                  appendKmConstructor(nextIndent, sb, constructor);
+                }));
+    appendKeyValue(
+        indent,
+        "contextReceiverTypes",
+        sb,
+        newIndent ->
+            appendKmList(
+                newIndent,
+                "KmType",
+                sb,
+                kmClass.getContextReceiverTypes(),
+                (nextIndent, kmType) -> appendKmType(nextIndent, sb, kmType)));
     appendKmDeclarationContainer(indent, sb, kmClass);
   }
 
@@ -458,6 +469,17 @@
                   appendKmContract(nextIndent, sb, contract);
                 });
           }
+          appendKeyValue(
+              newIndent,
+              "contextReceiverTypes",
+              sb,
+              newNewIndent ->
+                  appendKmList(
+                      newNewIndent,
+                      "KmType",
+                      sb,
+                      function.getContextReceiverTypes(),
+                      (nextIndent, kmType) -> appendKmType(nextIndent, sb, kmType)));
           JvmMethodSignature signature = JvmExtensionsKt.getSignature(function);
           appendKeyValue(
               newIndent, "signature", sb, signature != null ? signature.asString() : "null");
@@ -500,6 +522,17 @@
               sb,
               nextIndent -> appendValueParameter(nextIndent, sb, kmProperty.getSetterParameter()));
           appendKmVersionRequirement(newIndent, sb, kmProperty.getVersionRequirements());
+          appendKeyValue(
+              newIndent,
+              "contextReceiverTypes",
+              sb,
+              newNewIndent ->
+                  appendKmList(
+                      newNewIndent,
+                      "KmType",
+                      sb,
+                      kmProperty.getContextReceiverTypes(),
+                      (nextIndent, kmType) -> appendKmType(nextIndent, sb, kmType)));
           appendKeyValue(newIndent, "jvmFlags", sb, JvmExtensionsKt.getJvmFlags(kmProperty) + "");
           JvmFieldSignature fieldSignature = JvmExtensionsKt.getFieldSignature(kmProperty);
           appendKeyValue(
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 2a89998..7f0b67f 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
@@ -4,7 +4,9 @@
 
 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;
@@ -14,8 +16,7 @@
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.List;
-import kotlinx.metadata.jvm.KotlinClassHeader;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlin.Metadata;
 import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassFacade;
 
 // Holds information about Metadata.MultiFileClassFace
@@ -55,7 +56,7 @@
   }
 
   @Override
-  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
     List<String> partClassNameStrings = new ArrayList<>(partClassNames.size());
     boolean rewritten = false;
     for (KotlinTypeReference partClassName : partClassNames) {
@@ -69,9 +70,10 @@
               appView,
               null);
     }
-    KotlinClassMetadata.MultiFileClassFacade.Writer writer =
-        new KotlinClassMetadata.MultiFileClassFacade.Writer();
-    return Pair.create(writer.write(partClassNameStrings).getHeader(), rewritten);
+    return Pair.create(
+        Companion.writeMultiFileClassFacade(partClassNameStrings, getCompatibleKotlinInfo(), 0)
+            .getAnnotationData(),
+        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 62460ae..a51defc 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -4,15 +4,17 @@
 
 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
@@ -63,13 +65,13 @@
   }
 
   @Override
-  public Pair<KotlinClassHeader, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
     KmPackage kmPackage = new KmPackage();
     boolean rewritten = packageInfo.rewrite(kmPackage, clazz, appView);
-    KotlinClassMetadata.MultiFileClassPart.Writer writer =
-        new KotlinClassMetadata.MultiFileClassPart.Writer();
-    kmPackage.accept(writer);
-    return Pair.create(writer.write(facadeClassName).getHeader(), rewritten);
+    return Pair.create(
+        Companion.writeMultiFileClassPart(kmPackage, facadeClassName, getCompatibleKotlinInfo(), 0)
+            .getAnnotationData(),
+        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 a0f0e81..721cd75 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -18,7 +18,6 @@
 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 {
@@ -77,18 +76,16 @@
     KotlinMetadataMembersTracker rewrittenReferences = new KotlinMetadataMembersTracker(appView);
     boolean rewritten =
         containerInfo.rewrite(
-            kmPackage::visitFunction,
-            kmPackage::visitProperty,
-            kmPackage::visitTypeAlias,
+            kmPackage.getFunctions()::add,
+            kmPackage.getProperties()::add,
+            kmPackage.getTypeAliases()::add,
             clazz,
             appView,
             rewrittenReferences);
-    JvmPackageExtensionVisitor extensionVisitor =
-        (JvmPackageExtensionVisitor) kmPackage.visitExtensions(JvmPackageExtensionVisitor.TYPE);
     rewritten |=
-        localDelegatedProperties.rewrite(extensionVisitor::visitLocalDelegatedProperty, appView);
-    extensionVisitor.visitModuleName(moduleName);
-    extensionVisitor.visitEnd();
+        localDelegatedProperties.rewrite(
+            JvmExtensionsKt.getLocalDelegatedProperties(kmPackage)::add, appView);
+    JvmExtensionsKt.setModuleName(kmPackage, moduleName);
     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 6d10139..fc8e889 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
@@ -4,6 +4,9 @@
 
 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;
@@ -11,15 +14,12 @@
 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 {
@@ -58,6 +58,8 @@
   private final KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations;
 
   private final KotlinJvmMethodSignatureInfo syntheticMethodForDelegate;
+  // Collection of context receiver types
+  private final List<KotlinTypeInfo> contextReceiverTypes;
 
   private KotlinPropertyInfo(
       int flags,
@@ -74,7 +76,8 @@
       KotlinJvmMethodSignatureInfo getterSignature,
       KotlinJvmMethodSignatureInfo setterSignature,
       KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations,
-      KotlinJvmMethodSignatureInfo syntheticMethodForDelegate) {
+      KotlinJvmMethodSignatureInfo syntheticMethodForDelegate,
+      List<KotlinTypeInfo> contextReceiverTypes) {
     this.flags = flags;
     this.getterFlags = getterFlags;
     this.setterFlags = setterFlags;
@@ -90,6 +93,7 @@
     this.setterSignature = setterSignature;
     this.syntheticMethodForAnnotations = syntheticMethodForAnnotations;
     this.syntheticMethodForDelegate = syntheticMethodForDelegate;
+    this.contextReceiverTypes = contextReceiverTypes;
   }
 
   public static KotlinPropertyInfo create(
@@ -113,7 +117,10 @@
         KotlinJvmMethodSignatureInfo.create(
             JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty), factory),
         KotlinJvmMethodSignatureInfo.create(
-            JvmExtensionsKt.getSyntheticMethodForDelegate(kmProperty), factory));
+            JvmExtensionsKt.getSyntheticMethodForDelegate(kmProperty), factory),
+        ListUtils.map(
+            kmProperty.getContextReceiverTypes(),
+            contextRecieverType -> KotlinTypeInfo.create(contextRecieverType, factory, reporter)));
   }
 
   @Override
@@ -138,60 +145,81 @@
     return setterSignature;
   }
 
+  boolean rewriteNoBacking(Consumer<KmProperty> consumer, AppView<?> appView) {
+    return rewrite(consumer, null, null, null, appView);
+  }
+
   boolean rewrite(
-      KmVisitorProviders.KmPropertyVisitorProvider visitorProvider,
+      Consumer<KmProperty> consumer,
       DexEncodedField field,
       DexEncodedMethod getter,
       DexEncodedMethod setter,
       AppView<?> appView) {
     // TODO(b/154348683): Flags again.
-    KmPropertyVisitor kmProperty = visitorProvider.get(flags, name, getterFlags, setterFlags);
+    KmProperty kmProperty =
+        consume(new KmProperty(flags, name, getterFlags, setterFlags), consumer);
     // TODO(b/154348149): ReturnType could have been merged to a subtype.
-    boolean rewritten = false;
-    if (returnType != null) {
-      rewritten = returnType.rewrite(kmProperty::visitReturnType, appView);
+    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);
     }
-    if (receiverParameterType != null) {
-      rewritten |= receiverParameterType.rewrite(kmProperty::visitReceiverParameterType, appView);
+    if (getterSignature != null) {
+      rewritten |=
+          getterSignature.rewrite(
+              newSignature -> JvmExtensionsKt.setGetterSignature(kmProperty, newSignature),
+              getter,
+              appView);
     }
-    if (setterParameter != null) {
-      rewritten |= setterParameter.rewrite(kmProperty::visitSetterParameter, appView);
+    if (setterSignature != null) {
+      rewritten |=
+          setterSignature.rewrite(
+              newSignature -> JvmExtensionsKt.setSetterSignature(kmProperty, newSignature),
+              setter,
+              appView);
     }
-    for (KotlinTypeParameterInfo typeParameter : typeParameters) {
-      rewritten |= typeParameter.rewrite(kmProperty::visitTypeParameter, 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);
-      }
-    }
+    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);
     return rewritten;
   }
 
@@ -207,6 +235,7 @@
       setterParameter.trace(definitionSupplier);
     }
     forEachApply(typeParameters, param -> param::trace, definitionSupplier);
+    forEachApply(contextReceiverTypes, type -> type::trace, definitionSupplier);
     if (fieldSignature != null) {
       fieldSignature.trace(definitionSupplier);
     }
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 fb9b55f..2fb5dd1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -4,14 +4,17 @@
 
 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 {
@@ -73,15 +76,17 @@
   }
 
   @Override
-  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);
+  public Pair<Metadata, Boolean> rewrite(DexClass clazz, AppView<?> appView) {
+    if (lambda == null) {
+      return Pair.create(
+          Companion.writeSyntheticClass(getCompatibleKotlinInfo(), 0).getAnnotationData(), false);
     }
-    return Pair.create(writer.write().getHeader(), rewritten);
+    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);
   }
 
   @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 8f89b9e..6bb9e59 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
@@ -4,6 +4,8 @@
 
 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;
@@ -12,8 +14,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 {
@@ -57,18 +59,20 @@
         KotlinVersionRequirementInfo.create(alias.getVersionRequirements()));
   }
 
-  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);
+  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);
     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 a20d86c..93735f4 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -4,6 +4,9 @@
 
 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;
@@ -15,11 +18,10 @@
 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 {
@@ -82,34 +84,28 @@
     return arguments.build();
   }
 
-  boolean rewrite(KmVisitorProviders.KmTypeVisitorProvider visitorProvider, AppView<?> appView) {
+  boolean rewrite(Consumer<KmType> consumer, AppView<?> appView) {
     // TODO(b/154348683): Check for correct flags
-    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);
-    }
+    KmType kmType = consume(new KmType(flags), consumer);
+    boolean rewritten = classifier.rewrite(kmType, appView);
     rewritten |=
-        flexibleTypeUpperBound.rewrite(kmTypeVisitor::visitFlexibleTypeUpperBound, appView);
+        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);
     if (annotations.isEmpty() && !isRaw) {
       return rewritten;
     }
-    JvmTypeExtensionVisitor extensionVisitor =
-        (JvmTypeExtensionVisitor) kmTypeVisitor.visitExtensions(JvmTypeExtensionVisitor.TYPE);
-    if (extensionVisitor != null) {
-      for (KotlinAnnotationInfo annotation : annotations) {
-        rewritten |= annotation.rewrite(extensionVisitor::visitAnnotation, appView);
-      }
-      extensionVisitor.visit(isRaw);
-    }
+    rewritten |=
+        rewriteList(
+            appView,
+            annotations,
+            JvmExtensionsKt.getAnnotations(kmType),
+            KotlinAnnotationInfo::rewrite);
+    JvmExtensionsKt.setRaw(kmType, 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 e6cc10f..03bfcb5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
@@ -4,6 +4,8 @@
 
 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;
@@ -13,12 +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.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 {
@@ -83,24 +84,21 @@
     return builder.build();
   }
 
-  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);
-      }
-    }
+  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);
     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 85791fe..5ac01e6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -9,6 +9,7 @@
 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;
 
@@ -34,15 +35,13 @@
     return variance == null && typeInfo == null;
   }
 
-  boolean rewrite(
-      KmVisitorProviders.KmTypeProjectionVisitorProvider visitorProvider,
-      KmVisitorProviders.KmTypeStarProjectionVisitorProvider starProjectionProvider,
-      AppView<?> appView) {
+  boolean rewrite(Consumer<KmTypeProjection> consumer, AppView<?> appView) {
     if (isStarProjection()) {
-      starProjectionProvider.get();
+      consumer.accept(KmTypeProjection.STAR);
       return false;
     } else {
-      return typeInfo.rewrite(flags -> visitorProvider.get(flags, variance), appView);
+      return typeInfo.rewrite(
+          kmType -> consumer.accept(new KmTypeProjection(variance, kmType)), 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 1e12db6..68a79f2 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -4,6 +4,9 @@
 
 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;
@@ -11,9 +14,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.
@@ -65,14 +68,15 @@
     return builder.build();
   }
 
-  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);
-    }
+  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);
     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 3ab63f9..c82b039 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinVersionRequirementInfo.java
@@ -6,20 +6,17 @@
 
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import kotlinx.metadata.KmVersion;
+import java.util.function.Consumer;
 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<KotlinVersionRequirementPoint> versionRequirements;
+  private final List<KmVersionRequirement> versionRequirements;
 
-  private KotlinVersionRequirementInfo(List<KotlinVersionRequirementPoint> versionRequirements) {
+  private KotlinVersionRequirementInfo(List<KmVersionRequirement> versionRequirements) {
     this.versionRequirements = versionRequirements;
   }
 
@@ -27,57 +24,14 @@
     if (kmVersionRequirements.isEmpty()) {
       return NO_VERSION_REQUIREMENTS;
     }
-    ImmutableList.Builder<KotlinVersionRequirementPoint> builder = ImmutableList.builder();
-    for (KmVersionRequirement kmVersionRequirement : kmVersionRequirements) {
-      builder.add(KotlinVersionRequirementPoint.create(kmVersionRequirement));
-    }
-    return new KotlinVersionRequirementInfo(builder.build());
+    return new KotlinVersionRequirementInfo(ImmutableList.copyOf(kmVersionRequirements));
   }
 
-  boolean rewrite(KmVisitorProviders.KmVersionRequirementVisitorProvider visitorProvider) {
+  boolean rewrite(Consumer<List<KmVersionRequirement>> consumer) {
     if (this == NO_VERSION_REQUIREMENTS) {
       return false;
     }
-    for (KotlinVersionRequirementPoint versionRequirement : versionRequirements) {
-      versionRequirement.rewrite(visitorProvider.get());
-    }
+    consumer.accept(versionRequirements);
     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/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 36f3b07..259f1a0 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MAX_SUPPORTED_VERSION;
 import static com.android.tools.r8.ToolHelper.isWindows;
+import static com.google.common.io.Files.getNameWithoutExtension;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
@@ -223,6 +224,10 @@
     return this;
   }
 
+  public KotlinCompilerTool enableExperimentalContextReceivers() {
+    return addArguments("-Xcontext-receivers");
+  }
+
   public KotlinCompilerTool addSourceFiles(Path... files) {
     return addSourceFiles(Arrays.asList(files));
   }
@@ -268,7 +273,8 @@
                   try {
                     // The Kotlin compiler does not require particular naming of files except for
                     // the extension, so just create a file called source.kt in a new directory.
-                    Path fileNamedKt = temp.newFolder().toPath().resolve("source.kt");
+                    String newFileName = getNameWithoutExtension(fileNotNamedKt.toString()) + ".kt";
+                    Path fileNamedKt = temp.newFolder().toPath().resolve(newFileName);
                     Files.copy(fileNotNamedKt, fileNamedKt);
                     return fileNamedKt;
                   } catch (IOException e) {
diff --git a/src/test/java/com/android/tools/r8/SanityCheck.java b/src/test/java/com/android/tools/r8/SanityCheck.java
index b82995c..38fa873 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.impl.extensions.MetadataExtensions";
+      "com.android.tools.r8.jetbrains.kotlinx.metadata.internal.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 11ad201..4f334aa 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 ->
-                    assertEqualMetadata(
+                    assertEqualMetadataWithStringPoolValidation(
                         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 55a048e..07539ed 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,6 +15,7 @@
 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;
@@ -31,8 +32,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 {
 
@@ -54,7 +55,7 @@
   static final String KT_FUNCTION1 = "Lkotlin/Function1;";
   static final String KT_COMPARABLE = "Lkotlin/Comparable;";
 
-  public void assertEqualMetadata(
+  public void assertEqualMetadataWithStringPoolValidation(
       CodeInspector originalInspector,
       CodeInspector rewrittenInspector,
       BiConsumer<Integer, Integer> addedStringsInspector) {
@@ -73,9 +74,11 @@
         continue;
       }
       assertNotNull(rewrittenMetadata);
-      KotlinClassHeader originalHeader = originalMetadata.getHeader();
-      KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
-      TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+      KotlinMetadataAnnotationWrapper originalHeader =
+          KotlinMetadataAnnotationWrapper.wrap(originalMetadata);
+      KotlinMetadataAnnotationWrapper rewrittenHeader =
+          KotlinMetadataAnnotationWrapper.wrap(rewrittenMetadata);
+      TestCase.assertEquals(originalHeader.kind(), rewrittenHeader.kind());
 
       // 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
@@ -87,8 +90,8 @@
                   .computeIfAbsent(
                       method.getFinalSignature().toDescriptor(), ignoreArgument(ArrayList::new))
                   .add(method.getFinalName()));
-      HashSet<String> originalStrings = new HashSet<>(Arrays.asList(originalHeader.getData2()));
-      HashSet<String> rewrittenStrings = new HashSet<>(Arrays.asList(rewrittenHeader.getData2()));
+      HashSet<String> originalStrings = new HashSet<>(Arrays.asList(originalHeader.data2()));
+      HashSet<String> rewrittenStrings = new HashSet<>(Arrays.asList(rewrittenHeader.data2()));
       rewrittenStrings.forEach(
           rewrittenString -> {
             if (originalStrings.contains(rewrittenString)) {
@@ -108,7 +111,7 @@
             }
             addedNonInitStrings.increment();
           });
-      assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+      assertEquals(originalHeader.packageName(), rewrittenHeader.packageName());
 
       String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
       String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
@@ -117,6 +120,57 @@
     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 5e5fc28..bcbc386 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,26 +4,13 @@
 
 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;
@@ -97,7 +84,13 @@
                 ProguardKeepAttributes.INNER_CLASSES,
                 ProguardKeepAttributes.ENCLOSING_METHOD)
             .compile()
-            .inspect(this::inspect)
+            // 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))))
             .writeToZip();
     Path main =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
@@ -112,33 +105,6 @@
         .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/MetadataRewriteContextReceiverTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteContextReceiverTest.java
new file mode 100644
index 0000000..102a056
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteContextReceiverTest.java
@@ -0,0 +1,193 @@
+// 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.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_8_0;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinTargetVersion.JAVA_8;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+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.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteContextReceiverTest extends KotlinMetadataTestBase {
+
+  private static final String PKG_LIB = PKG + ".context_receiver_lib";
+  private static final String PKG_APP = PKG + ".context_receiver_app";
+  private static final Path LIB_FILE =
+      getFileInTest(PKG_PREFIX + "/context_receiver_lib", "lib.txt");
+  private static final Path MAIN_FILE =
+      getFileInTest(PKG_PREFIX + "/context_receiver_app", "main.txt");
+  private static final String MAIN = PKG_APP + ".MainKt";
+  private final TestParameters parameters;
+
+  private static final String EXPECTED =
+      StringUtils.lines(
+          "FooImpl::m1",
+          "BarImpl::m2",
+          "BazImpl::m3",
+          "BazImpl::m3",
+          "FooImpl::m1",
+          "BarImpl::m2",
+          "Hello World!");
+
+  @Parameterized.Parameters(name = "{0}, {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(),
+        getKotlinTestParameters()
+            .withCompilersStartingFromIncluding(KOTLINC_1_8_0)
+            .withOldCompilersStartingFrom(KOTLINC_1_8_0)
+            .withTargetVersion(JAVA_8)
+            .build());
+  }
+
+  public MetadataRewriteContextReceiverTest(
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
+    this.parameters = parameters;
+  }
+
+  private static final KotlinCompileMemoizer libJars =
+      getCompileMemoizer()
+          .configure(
+              kotlinc ->
+                  kotlinc
+                      .addSourceFilesWithNonKtExtension(getStaticTemp(), LIB_FILE)
+                      .enableExperimentalContextReceivers());
+
+  @Test
+  public void smokeTest() throws Exception {
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+            .addSourceFilesWithNonKtExtension(temp, MAIN_FILE)
+            .setOutputPath(temp.newFolder().toPath())
+            .enableExperimentalContextReceivers()
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(
+            kotlinc.getKotlinStdlibJar(),
+            kotlinc.getKotlinReflectJar(),
+            libJars.getForConfiguration(kotlinc, targetVersion))
+        .addClasspath(output)
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testMetadataForCompilationWithKeepAll() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(kotlinc.getKotlinAnnotationJar())
+            .addProgramFiles(
+                libJars.getForConfiguration(kotlinc, targetVersion), kotlinc.getKotlinStdlibJar())
+            .addKeepClassAndMembersRules(PKG_LIB + ".*")
+            .addKeepAttributes(
+                ProguardKeepAttributes.SIGNATURE,
+                ProguardKeepAttributes.INNER_CLASSES,
+                ProguardKeepAttributes.ENCLOSING_METHOD)
+            .addKeepKotlinMetadata()
+            .allowDiagnosticWarningMessages()
+            .addOptionsModification(
+                options -> options.testing.keepMetadataInR8IfNotRewritten = false)
+            .compile()
+            // Since this has a keep-all classes rule assert that the meta-data is equal to the
+            // original one.
+            .inspect(
+                inspector ->
+                    assertEqualDeserializedMetadata(
+                        inspector,
+                        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion))))
+            .writeToZip();
+    Path main =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFilesWithNonKtExtension(temp, MAIN_FILE)
+            .setOutputPath(temp.newFolder().toPath())
+            .enableExperimentalContextReceivers()
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
+        .addClasspath(main)
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testMetadataInExtensionFunction_renamedKotlinSources() throws Exception {
+    R8TestCompileResult r8LibraryResult =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+            .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
+            // Ensure that we do not rename members
+            .addKeepRules("-keepclassmembers class * { *; }")
+            // Keep the Foo class but rename it.
+            .addKeepRules("-keep,allowobfuscation class " + PKG_LIB + ".Foo")
+            // Keep the Bar class but rename it.
+            .addKeepRules("-keep,allowobfuscation class " + PKG_LIB + ".Bar")
+            // Keep the Baz class but rename it.
+            .addKeepRules("-keep,allowobfuscation class " + PKG_LIB + ".Baz")
+            // Keep all Printer fields.
+            .addKeepRules("-keep class " + PKG_LIB + ".Printer { *; }")
+            // Keep Super, but allow minification.
+            .addKeepRules("-keep class " + PKG_LIB + ".LibKt { <methods>; }")
+            .addKeepKotlinMetadata()
+            .compile();
+
+    // Rewrite the kotlin source to rewrite the classes from the mapping file
+    String kotlinSource = FileUtils.readTextFile(MAIN_FILE, StandardCharsets.UTF_8);
+
+    CodeInspector inspector = r8LibraryResult.inspector();
+    // Rewrite the source kotlin file that reference the renamed classes uses in the context
+    // receivers.
+    for (String className : new String[] {"Foo", "Bar", "Baz"}) {
+      String originalClassName = PKG_LIB + "." + className;
+      ClassSubject clazz = inspector.clazz(originalClassName);
+      assertThat(clazz, isPresentAndRenamed());
+      kotlinSource =
+          kotlinSource.replace("import " + originalClassName, "import " + clazz.getFinalName());
+      kotlinSource =
+          kotlinSource.replace(
+              ": " + className + " {",
+              ": "
+                  + DescriptorUtils.getSimpleClassNameFromDescriptor(clazz.getFinalDescriptor())
+                  + " {");
+    }
+
+    Path newSource = temp.newFolder().toPath().resolve("main.kt");
+    Files.write(newSource, kotlinSource.getBytes(StandardCharsets.UTF_8));
+
+    Path libJar = r8LibraryResult.writeToZip();
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(newSource)
+            .setOutputPath(temp.newFolder().toPath())
+            .enableExperimentalContextReceivers()
+            .compile();
+
+    testForJvm()
+        .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+}
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 af22ae1..0704ecd 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,24 +5,15 @@
 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;
@@ -84,7 +75,11 @@
                 "-keep class " + PKG + ".inline_class_lib.LibKt { *** login-*(java.lang.String); }")
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
             .compile()
-            .inspect(this::inspect)
+            .inspect(
+                inspector ->
+                    assertEqualMetadata(
+                        inspector,
+                        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion))))
             .writeToZip();
     Path main =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
@@ -98,24 +93,4 @@
         .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 c693a41..6f78827 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,24 +4,13 @@
 
 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;
@@ -78,7 +67,11 @@
                 ProguardKeepAttributes.INNER_CLASSES,
                 ProguardKeepAttributes.ENCLOSING_METHOD)
             .compile()
-            .inspect(this::inspect)
+            .inspect(
+                inspector ->
+                    assertEqualDeserializedMetadata(
+                        inspector,
+                        new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion))))
             .writeToZip();
     Path main =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
@@ -92,29 +85,4 @@
         .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 24b3e58..ce9fba4 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,10 +10,12 @@
 
 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;
@@ -68,8 +70,9 @@
       if (clazz.getFinalName().startsWith("kotlin.io")
           || clazz.getFinalName().equals("kotlin.Metadata")
           || clazz.getFinalName().equals("kotlin.jvm.JvmName")) {
-        assertNotNull(clazz.getKotlinClassMetadata());
-        assertNotNull(clazz.getKotlinClassMetadata().getHeader().getData2());
+        KotlinClassMetadata kotlinClassMetadata = clazz.getKotlinClassMetadata();
+        assertNotNull(kotlinClassMetadata);
+        assertNotNull(KotlinMetadataAnnotationWrapper.wrap(kotlinClassMetadata).data2());
       } 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 2a7ee54..9fddbf0 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 ->
-                    assertEqualMetadata(
+                    assertEqualMetadataWithStringPoolValidation(
                         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 d08e0a0..e06e1ba 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 ->
-                assertEqualMetadata(
+                assertEqualMetadataWithStringPoolValidation(
                     new CodeInspector(kotlinc.getKotlinStdlibJar()),
                     inspector,
                     (addedStrings, addedNonInitStrings) -> {
@@ -74,7 +74,7 @@
         .compile()
         .inspect(
             inspector ->
-                assertEqualMetadata(
+                assertEqualMetadataWithStringPoolValidation(
                     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 7686ac3..6012fb0 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 ->
-                    assertEqualMetadata(
+                    assertEqualMetadataWithStringPoolValidation(
                         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 5d13395..4683d0b 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,27 +6,16 @@
 
 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;
@@ -109,7 +98,12 @@
             .compile()
             .assertAllWarningMessagesMatch(
                 equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
-            .inspect(this::inspect)
+            .inspect(
+                inspector ->
+                    assertEqualMetadata(
+                        inspector,
+                        new CodeInspector(
+                            kotlincLibJar.getForConfiguration(kotlinc, targetVersion))))
             .writeToZip();
     Path main =
         kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
@@ -124,29 +118,4 @@
         .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 6aecc85..b65643f 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,7 +9,6 @@
 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;
@@ -20,13 +19,9 @@
 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;
@@ -112,31 +107,13 @@
   }
 
   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());
-      // 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"));
-      }
-    }
+    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"));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/context_receiver_app/main.txt b/src/test/java/com/android/tools/r8/kotlin/metadata/context_receiver_app/main.txt
new file mode 100644
index 0000000..9380c6f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/context_receiver_app/main.txt
@@ -0,0 +1,46 @@
+// 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.metadata.context_receiver_app
+
+import com.android.tools.r8.kotlin.metadata.context_receiver_lib.Bar
+import com.android.tools.r8.kotlin.metadata.context_receiver_lib.Foo
+import com.android.tools.r8.kotlin.metadata.context_receiver_lib.Baz
+import com.android.tools.r8.kotlin.metadata.context_receiver_lib.Printer
+import com.android.tools.r8.kotlin.metadata.context_receiver_lib.callFooBar
+
+class FooImpl : Foo {
+
+  override fun m1(): String {
+    println("FooImpl::m1")
+    return "Hello "
+  }
+}
+
+class BarImpl : Bar {
+  override fun m2(): String {
+    println("BarImpl::m2")
+    return "World!"
+  }
+}
+
+class BazImpl : Baz {
+  override fun m3(): String {
+    println("BazImpl::m3")
+    return "BazImpl::m3"
+  }
+}
+
+fun main() {
+  with (FooImpl()) {
+    with (BarImpl()) {
+      val printer = Printer()
+      printer.fooBar()
+      with (BazImpl()) {
+        println(printer.getValue)
+        println(callFooBar())
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/context_receiver_lib/lib.txt b/src/test/java/com/android/tools/r8/kotlin/metadata/context_receiver_lib/lib.txt
new file mode 100644
index 0000000..0235848
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/context_receiver_lib/lib.txt
@@ -0,0 +1,35 @@
+// 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.metadata.context_receiver_lib
+
+interface Foo {
+  fun m1(): String
+}
+
+interface Bar {
+  fun m2(): String
+}
+
+interface Baz {
+  fun m3(): String
+}
+
+context(Foo, Bar)
+class Printer {
+
+  fun fooBar() {
+    m1();
+    m2();
+  }
+
+  context(Baz)
+  val getValue: String
+    get() = if (System.currentTimeMillis() == 0L) "foo" else m3()
+}
+
+context(Foo, Bar)
+fun callFooBar() : String {
+  return m1() + m2();
+}
\ No newline at end of file