Reland "Inspect kotlin metadata proto messages before modeling signatures"

Bug: 185756596
Change-Id: Ic2bbcd75db40a5f140bc3a25ffb6e042e359cf59
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 f10902f..c3bb323 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -84,13 +84,16 @@
   }
 
   public static KotlinClassInfo create(
-      KmClass kmClass,
+      KotlinClassMetadata.Class metadata,
       String packageName,
       int[] metadataVersion,
       DexClass hostClass,
       DexItemFactory factory,
       Reporter reporter,
       Consumer<DexEncodedMethod> keepByteCode) {
+    KmClass kmClass = metadata.toKmClass();
+    KotlinJvmSignatureExtensionInformation extensionInformation =
+        KotlinJvmSignatureExtensionInformation.readInformationFromMessage(metadata);
     Map<String, DexEncodedField> fieldMap = new HashMap<>();
     for (DexEncodedField field : hostClass.fields()) {
       fieldMap.put(toJvmFieldSignature(field.getReference()).asString(), field);
@@ -100,9 +103,12 @@
       methodMap.put(toJvmMethodSignature(method.getReference()).asString(), method);
     }
     ImmutableList.Builder<KotlinConstructorInfo> notBackedConstructors = ImmutableList.builder();
+    int constructorIndex = 0;
     for (KmConstructor kmConstructor : kmClass.getConstructors()) {
+      boolean readConstructorSignature =
+          extensionInformation.hasJvmMethodSignatureExtensionForConstructor(constructorIndex++);
       KotlinConstructorInfo constructorInfo =
-          KotlinConstructorInfo.create(kmConstructor, factory, reporter);
+          KotlinConstructorInfo.create(kmConstructor, factory, reporter, readConstructorSignature);
       JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmConstructor);
       if (signature != null) {
         DexEncodedMethod method = methodMap.get(signature.asString());
@@ -116,7 +122,7 @@
     }
     KotlinDeclarationContainerInfo container =
         KotlinDeclarationContainerInfo.create(
-            kmClass, methodMap, fieldMap, factory, reporter, keepByteCode);
+            kmClass, methodMap, fieldMap, factory, reporter, keepByteCode, extensionInformation);
     setCompanionObject(kmClass, hostClass, reporter);
     return new KotlinClassInfo(
         kmClass.getFlags(),
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 6fc09f5..cb0a7e8 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -27,6 +27,9 @@
 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;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassPart;
 import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
 
 public final class KotlinClassMetadataReader {
@@ -139,7 +142,7 @@
     int[] metadataVersion = kMetadata.getHeader().getMetadataVersion();
     if (kMetadata instanceof KotlinClassMetadata.Class) {
       return KotlinClassInfo.create(
-          ((KotlinClassMetadata.Class) kMetadata).toKmClass(),
+          (KotlinClassMetadata.Class) kMetadata,
           packageName,
           metadataVersion,
           clazz,
@@ -149,7 +152,7 @@
     } else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
       // e.g., B.kt becomes class `BKt`
       return KotlinFileFacadeInfo.create(
-          (KotlinClassMetadata.FileFacade) kMetadata,
+          (FileFacade) kMetadata,
           packageName,
           metadataVersion,
           clazz,
@@ -159,14 +162,11 @@
     } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
       // multi-file class with the same @JvmName.
       return KotlinMultiFileClassFacadeInfo.create(
-          (KotlinClassMetadata.MultiFileClassFacade) kMetadata,
-          packageName,
-          metadataVersion,
-          factory);
+          (MultiFileClassFacade) kMetadata, packageName, metadataVersion, factory);
     } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
       // A single file, which is part of multi-file class.
       return KotlinMultiFileClassPartInfo.create(
-          (KotlinClassMetadata.MultiFileClassPart) kMetadata,
+          (MultiFileClassPart) kMetadata,
           packageName,
           metadataVersion,
           clazz,
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 2e4ee98..eb8421a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -41,12 +41,18 @@
   }
 
   public static KotlinConstructorInfo create(
-      KmConstructor kmConstructor, DexItemFactory factory, Reporter reporter) {
+      KmConstructor kmConstructor,
+      DexItemFactory factory,
+      Reporter reporter,
+      boolean readConstructorSignature) {
     return new KotlinConstructorInfo(
         kmConstructor.getFlags(),
         KotlinValueParameterInfo.create(kmConstructor.getValueParameters(), factory, reporter),
         KotlinVersionRequirementInfo.create(kmConstructor.getVersionRequirements()),
-        KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmConstructor), factory));
+        readConstructorSignature
+            ? KotlinJvmMethodSignatureInfo.create(
+                JvmExtensionsKt.getSignature(kmConstructor), factory)
+            : null);
   }
 
   public void 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 7447bbd..92db593 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -55,8 +55,10 @@
       Map<String, DexEncodedField> fieldSignatureMap,
       DexItemFactory factory,
       Reporter reporter,
-      Consumer<DexEncodedMethod> keepByteCode) {
+      Consumer<DexEncodedMethod> keepByteCode,
+      KotlinJvmSignatureExtensionInformation extensionInformation) {
     ImmutableList.Builder<KotlinFunctionInfo> notBackedFunctions = ImmutableList.builder();
+    int functionCounter = 0;
     for (KmFunction kmFunction : container.getFunctions()) {
       JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmFunction);
       if (signature == null) {
@@ -64,7 +66,11 @@
         continue;
       }
       KotlinFunctionInfo kotlinFunctionInfo =
-          KotlinFunctionInfo.create(kmFunction, factory, reporter);
+          KotlinFunctionInfo.create(
+              kmFunction,
+              factory,
+              reporter,
+              extensionInformation.hasJvmMethodSignatureExtensionForFunction(functionCounter++));
       DexEncodedMethod method = methodSignatureMap.get(signature.asString());
       if (method == null) {
         notBackedFunctions.add(kotlinFunctionInfo);
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 b012758..abb6ff1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -39,9 +39,12 @@
       DexItemFactory factory,
       Reporter reporter,
       Consumer<DexEncodedMethod> keepByteCode) {
+    KmPackage kmPackage = kmFileFacade.toKmPackage();
+    KotlinJvmSignatureExtensionInformation extensionInformation =
+        KotlinJvmSignatureExtensionInformation.readInformationFromMessage(kmFileFacade);
     return new KotlinFileFacadeInfo(
         KotlinPackageInfo.create(
-            kmFileFacade.toKmPackage(), clazz, factory, reporter, keepByteCode),
+            kmPackage, clazz, factory, reporter, keepByteCode, extensionInformation),
         packageName,
         metadataVersion);
   }
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 008177e..057f8bc 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -73,7 +73,10 @@
   }
 
   static KotlinFunctionInfo create(
-      KmFunction kmFunction, DexItemFactory factory, Reporter reporter) {
+      KmFunction kmFunction,
+      DexItemFactory factory,
+      Reporter reporter,
+      boolean readMethodSignature) {
     boolean isCrossInline = false;
     List<KotlinValueParameterInfo> valueParameters =
         KotlinValueParameterInfo.create(kmFunction.getValueParameters(), factory, reporter);
@@ -90,7 +93,9 @@
         KotlinTypeInfo.create(kmFunction.getReceiverParameterType(), factory, reporter),
         valueParameters,
         KotlinTypeParameterInfo.create(kmFunction.getTypeParameters(), factory, reporter),
-        KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), factory),
+        readMethodSignature
+            ? KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), factory)
+            : null,
         getlambdaClassOrigin(kmFunction, factory),
         KotlinVersionRequirementInfo.create(kmFunction.getVersionRequirements()),
         KotlinContractInfo.create(kmFunction.getContract(), factory, reporter),
@@ -106,6 +111,10 @@
     return null;
   }
 
+  public String getName() {
+    return name;
+  }
+
   public void rewrite(
       KmVisitorProviders.KmFunctionVisitorProvider visitorProvider,
       DexEncodedMethod method,
@@ -162,10 +171,6 @@
     return receiverParameterType != null;
   }
 
-  public KotlinJvmMethodSignatureInfo getSignature() {
-    return signature;
-  }
-
   @Override
   public void trace(DexDefinitionSupplier definitionSupplier) {
     forEachApply(valueParameters, param -> param::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 b0314be..400e66b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -50,25 +50,24 @@
     if (methodSignature == null) {
       return null;
     }
-    String kotlinDescriptor = methodSignature.getDesc();
-    if (!KotlinMetadataUtils.isValidMethodDescriptor(kotlinDescriptor)) {
+    String name = methodSignature.getName();
+    String descriptor = methodSignature.getDesc();
+    if (!KotlinMetadataUtils.isValidMethodDescriptor(descriptor)) {
       // If the method descriptor is invalid, keep it as invalid.
-      return new KotlinJvmMethodSignatureInfo(methodSignature.getName(), kotlinDescriptor);
+      return new KotlinJvmMethodSignatureInfo(methodSignature.getName(), descriptor);
     }
-    String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(kotlinDescriptor);
+    String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(descriptor);
     KotlinTypeReference returnType =
         KotlinTypeReference.fromDescriptor(returnTypeDescriptor, factory);
-    String[] descriptors = DescriptorUtils.getArgumentTypeDescriptors(kotlinDescriptor);
+    String[] descriptors = DescriptorUtils.getArgumentTypeDescriptors(descriptor);
     if (descriptors.length == 0) {
-      return new KotlinJvmMethodSignatureInfo(
-          methodSignature.getName(), returnType, EMPTY_PARAMETERS_LIST);
+      return new KotlinJvmMethodSignatureInfo(name, returnType, EMPTY_PARAMETERS_LIST);
     }
     ImmutableList.Builder<KotlinTypeReference> parameters = ImmutableList.builder();
-    for (String descriptor : descriptors) {
-      parameters.add(KotlinTypeReference.fromDescriptor(descriptor, factory));
+    for (String paramDescriptor : descriptors) {
+      parameters.add(KotlinTypeReference.fromDescriptor(paramDescriptor, factory));
     }
-    return new KotlinJvmMethodSignatureInfo(
-        methodSignature.getName(), returnType, parameters.build());
+    return new KotlinJvmMethodSignatureInfo(name, returnType, parameters.build());
   }
 
   public JvmMethodSignature rewrite(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java
new file mode 100644
index 0000000..955c1ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmSignatureExtensionInformation.java
@@ -0,0 +1,170 @@
+// Copyright (c) 2021, 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.utils.ListUtils;
+import com.android.tools.r8.utils.ReflectionHelper;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import kotlin.Pair;
+import kotlinx.metadata.internal.metadata.ProtoBuf;
+import kotlinx.metadata.internal.metadata.jvm.JvmProtoBuf;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassPart;
+import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
+
+// Due to kotlin-metadata-jvm library synthesizing jvm method signatures from type-information
+// we do an extra check to figure out if we should model the signature. If we model it, we will
+// add name and descriptor information to the string pool for the proto message, so it is
+// better to avoid it.
+// https://github.com/Kotlin/kotlinx.reflect.lite/blob/46d47f118f9846166b6b8f8212bdbc822fe2f634/src/main/java/org/jetbrains/kotlin/serialization/jvm/JvmProtoBufUtil.kt
+
+public class KotlinJvmSignatureExtensionInformation {
+
+  private final Set<Integer> noExtensionIndicesForFunctions;
+  private final Set<Integer> noExtensionIndicesForConstructors;
+
+  private static final KotlinJvmSignatureExtensionInformation EMPTY = builder().build();
+
+  private KotlinJvmSignatureExtensionInformation(
+      Set<Integer> noExtensionIndicesForFunctions, Set<Integer> noExtensionIndicesForConstructors) {
+    this.noExtensionIndicesForFunctions = noExtensionIndicesForFunctions;
+    this.noExtensionIndicesForConstructors = noExtensionIndicesForConstructors;
+  }
+
+  public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+      FileFacade fileFacadeMetadata) {
+    return readPackageDataFromMessage(fileFacadeMetadata);
+  }
+
+  public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+      MultiFileClassPart classPart) {
+    return readPackageDataFromMessage(classPart);
+  }
+
+  private static KotlinJvmSignatureExtensionInformation readPackageDataFromMessage(Object object) {
+    try {
+      Pair<?, ProtoBuf.Package> kotlinPairData =
+          ReflectionHelper.performReflection(
+              object,
+              ReflectionHelper.builder()
+                  .readField("packageData$delegate")
+                  .setSetAccessible(true)
+                  .done()
+                  .readMethod("getValue")
+                  .setSetAccessible(true)
+                  .done()
+                  .build());
+      return builder().visit(kotlinPairData.getSecond()).build();
+    } catch (Exception e) {
+      return empty();
+    }
+  }
+
+  public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+      SyntheticClass syntheticClass) {
+    try {
+      Pair<?, ProtoBuf.Function> kotlinPairData =
+          ReflectionHelper.performReflection(
+              syntheticClass,
+              ReflectionHelper.builder()
+                  .readField("functionData$delegate")
+                  .setSetAccessible(true)
+                  .done()
+                  .readMethod("getValue")
+                  .setSetAccessible(true)
+                  .done()
+                  .build());
+      if (kotlinPairData == null) {
+        return empty();
+      }
+      return builder().visit(kotlinPairData.getSecond(), 0).build();
+    } catch (Exception e) {
+      return empty();
+    }
+  }
+
+  public static KotlinJvmSignatureExtensionInformation readInformationFromMessage(
+      KotlinClassMetadata.Class kMetadata) {
+    try {
+      Pair<?, ProtoBuf.Class> kotlinPairData =
+          ReflectionHelper.performReflection(
+              kMetadata,
+              ReflectionHelper.builder()
+                  .readField("classData$delegate")
+                  .setSetAccessible(true)
+                  .done()
+                  .readMethod("getValue")
+                  .setSetAccessible(true)
+                  .done()
+                  .build());
+      return builder().visit(kotlinPairData.getSecond()).build();
+    } catch (Exception e) {
+      return empty();
+    }
+  }
+
+  public boolean hasJvmMethodSignatureExtensionForFunction(int index) {
+    return !noExtensionIndicesForFunctions.contains(index);
+  }
+
+  public boolean hasJvmMethodSignatureExtensionForConstructor(int index) {
+    return !noExtensionIndicesForConstructors.contains(index);
+  }
+
+  public static KotlinJvmSignatureExtensionInformationBuilder builder() {
+    return new KotlinJvmSignatureExtensionInformationBuilder();
+  }
+
+  public static KotlinJvmSignatureExtensionInformation empty() {
+    return EMPTY;
+  }
+
+  private static class KotlinJvmSignatureExtensionInformationBuilder {
+
+    private final Set<Integer> noExtensionIndicesForFunctions = new HashSet<>();
+    private final Set<Integer> noExtensionIndicesForConstructors = new HashSet<>();
+
+    private KotlinJvmSignatureExtensionInformation build() {
+      return new KotlinJvmSignatureExtensionInformation(
+          noExtensionIndicesForFunctions, noExtensionIndicesForConstructors);
+    }
+
+    private KotlinJvmSignatureExtensionInformationBuilder visit(ProtoBuf.Class clazz) {
+      visitFunctions(clazz.getFunctionList());
+      visitConstructors(clazz.getConstructorList());
+      return this;
+    }
+
+    private KotlinJvmSignatureExtensionInformationBuilder visit(ProtoBuf.Package pkg) {
+      visitFunctions(pkg.getFunctionList());
+      return this;
+    }
+
+    private void visitFunctions(List<ProtoBuf.Function> functions) {
+      ListUtils.forEachWithIndex(functions, this::visit);
+    }
+
+    public KotlinJvmSignatureExtensionInformationBuilder visit(
+        ProtoBuf.Function function, int index) {
+      if (!function.hasExtension(JvmProtoBuf.methodSignature)) {
+        noExtensionIndicesForFunctions.add(index);
+      }
+      return this;
+    }
+
+    private void visitConstructors(List<ProtoBuf.Constructor> constructors) {
+      ListUtils.forEachWithIndex(
+          constructors,
+          (constructor, index) -> {
+            if (!constructor.hasExtension(JvmProtoBuf.constructorSignature)) {
+              noExtensionIndicesForConstructors.add(index);
+            }
+          });
+    }
+  }
+}
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 01a7c2a..df28ccf 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -30,13 +30,21 @@
   }
 
   static KotlinLambdaInfo create(
-      DexClass clazz, KmLambda lambda, DexItemFactory factory, Reporter reporter) {
+      DexClass clazz,
+      KmLambda lambda,
+      DexItemFactory factory,
+      Reporter reporter,
+      KotlinJvmSignatureExtensionInformation extensionInformation) {
     if (lambda == null) {
       assert false;
       return null;
     }
     KotlinFunctionInfo kotlinFunctionInfo =
-        KotlinFunctionInfo.create(lambda.function, factory, reporter);
+        KotlinFunctionInfo.create(
+            lambda.function,
+            factory,
+            reporter,
+            extensionInformation.hasJvmMethodSignatureExtensionForFunction(0));
     JvmMethodSignature signature = JvmExtensionsKt.getSignature(lambda.function);
     if (signature != null) {
       for (DexEncodedMethod method : clazz.methods()) {
@@ -69,8 +77,7 @@
       appView
           .options()
           .reporter
-          .info(
-              KotlinMetadataDiagnostic.lambdaBackingNotFound(clazz.type, function.getSignature()));
+          .info(KotlinMetadataDiagnostic.lambdaBackingNotFound(clazz.type, function.getName()));
       return false;
     }
     function.rewrite(visitorProvider.get()::visitFunction, backing, appView, namingLens);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
index 3415e13..6c4c963 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
@@ -74,13 +74,12 @@
             + StringUtils.stacktraceAsString(t));
   }
 
-  static KotlinMetadataDiagnostic lambdaBackingNotFound(
-      DexType type, KotlinJvmMethodSignatureInfo signatureInfo) {
+  static KotlinMetadataDiagnostic lambdaBackingNotFound(DexType type, String functionName) {
     return new KotlinMetadataDiagnostic(
         Origin.unknown(),
         Position.UNKNOWN,
         "The lambda function "
-            + signatureInfo.toString()
+            + functionName
             + " could no longer be found in "
             + type.toSourceString()
             + " . The method is most likely pruned and would require a specific keep rule to keep"
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 4eef6cb..d092bff 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -84,7 +84,6 @@
               }
             }
           });
-      appView.setCfByteCodePassThrough(keepByteCodeFunctions);
       for (DexProgramClass localOrAnonymousClass : localOrAnonymousClasses) {
         EnclosingMethodAttribute enclosingAttribute =
             localOrAnonymousClass.getEnclosingMethodAttribute();
@@ -103,6 +102,7 @@
               m -> keepByteCodeFunctions.add(m.getReference()));
         }
       }
+      appView.setCfByteCodePassThrough(keepByteCodeFunctions);
     } else {
       assert verifyKotlinMetadataModeledForAllClasses(enqueuer, keepMetadata);
     }
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 4a5904e..ee582bb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -101,11 +101,11 @@
         appView.appInfo().classes(),
         clazz -> {
           KotlinClassLevelInfo kotlinInfo = clazz.getKotlinInfo();
-          DexAnnotation oldMeta = clazz.annotations().getFirstMatching(rewrittenMetadataType);
           if (kotlinInfo == INVALID_KOTLIN_INFO) {
             // Maintain invalid kotlin info for classes.
             return;
           }
+          DexAnnotation oldMeta = clazz.annotations().getFirstMatching(rewrittenMetadataType);
           // TODO(b/181103083): Consider removing if rewrittenMetadataType
           //  != factory.kotlinMetadataType
           if (oldMeta == null
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 6b99d15..e0db449 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -45,9 +45,13 @@
       DexItemFactory factory,
       Reporter reporter,
       Consumer<DexEncodedMethod> keepByteCode) {
+    KmPackage kmPackage = classPart.toKmPackage();
+    KotlinJvmSignatureExtensionInformation extensionInformation =
+        KotlinJvmSignatureExtensionInformation.readInformationFromMessage(classPart);
     return new KotlinMultiFileClassPartInfo(
         classPart.getFacadeClassName(),
-        KotlinPackageInfo.create(classPart.toKmPackage(), clazz, factory, reporter, keepByteCode),
+        KotlinPackageInfo.create(
+            kmPackage, clazz, factory, reporter, keepByteCode, extensionInformation),
         packageName,
         metadataVersion);
   }
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 41ff17d..397d0fa 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -44,7 +44,8 @@
       DexClass clazz,
       DexItemFactory factory,
       Reporter reporter,
-      Consumer<DexEncodedMethod> keepByteCode) {
+      Consumer<DexEncodedMethod> keepByteCode,
+      KotlinJvmSignatureExtensionInformation extensionInformation) {
     Map<String, DexEncodedField> fieldMap = new HashMap<>();
     for (DexEncodedField field : clazz.fields()) {
       fieldMap.put(toJvmFieldSignature(field.getReference()).asString(), field);
@@ -56,7 +57,7 @@
     return new KotlinPackageInfo(
         JvmExtensionsKt.getModuleName(kmPackage),
         KotlinDeclarationContainerInfo.create(
-            kmPackage, methodMap, fieldMap, factory, reporter, keepByteCode),
+            kmPackage, methodMap, fieldMap, factory, reporter, keepByteCode, extensionInformation),
         KotlinLocalDelegatedPropertyInfo.create(
             JvmExtensionsKt.getLocalDelegatedProperties(kmPackage), factory, reporter));
   }
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 36bf5cf..b2381de 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -47,13 +47,14 @@
       Kotlin kotlin,
       DexItemFactory factory,
       Reporter reporter) {
-    KmLambda lambda = null;
-    if (syntheticClass.isLambda()) {
-      lambda = syntheticClass.toKmLambda();
-      assert lambda != null;
-    }
+    KmLambda lambda = syntheticClass.toKmLambda();
+    assert lambda == null || syntheticClass.isLambda();
+    KotlinJvmSignatureExtensionInformation extensionInformation =
+        KotlinJvmSignatureExtensionInformation.readInformationFromMessage(syntheticClass);
     return new KotlinSyntheticClassInfo(
-        lambda != null ? KotlinLambdaInfo.create(clazz, lambda, factory, reporter) : null,
+        lambda != null
+            ? KotlinLambdaInfo.create(clazz, lambda, factory, reporter, extensionInformation)
+            : null,
         getFlavour(syntheticClass, clazz, kotlin),
         packageName,
         metadataVersion);
diff --git a/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java b/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java
new file mode 100644
index 0000000..5e1cdbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ReflectionHelper.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class ReflectionHelper {
+
+  @SuppressWarnings("unchecked")
+  public static <T> T performReflection(Object object, ReflectiveOperation<?> operation)
+      throws Exception {
+    return (T) operation.compute(object);
+  }
+
+  public static ReflectiveOperationSequenceBuilder builder() {
+    return new ReflectiveOperationSequenceBuilder();
+  }
+
+  public enum DeclaredType {
+    FIELD,
+    METHOD
+  }
+
+  public abstract static class ReflectiveOperation<Member> {
+
+    final Class<?> classForDeclaration;
+    final String declaredMember;
+    final Consumer<Member> modifier;
+    final ReflectiveOperation<?> nextOperation;
+
+    private ReflectiveOperation(
+        Class<?> classForDeclaration,
+        String declaredMember,
+        ReflectiveOperation<?> nextOperation,
+        Consumer<Member> modifier) {
+      this.classForDeclaration = classForDeclaration;
+      this.declaredMember = declaredMember;
+      this.nextOperation = nextOperation;
+      this.modifier = modifier;
+    }
+
+    public abstract Object compute(Object object) throws Exception;
+  }
+
+  public static class ReflectiveMethodOperation extends ReflectiveOperation<Method> {
+
+    private ReflectiveMethodOperation(
+        Class<?> classForDeclaration,
+        String declaredMember,
+        ReflectiveOperation<?> nextOperation,
+        Consumer<Method> modifier) {
+      super(classForDeclaration, declaredMember, nextOperation, modifier);
+    }
+
+    @Override
+    public Object compute(Object object) throws Exception {
+      Class<?> clazz = classForDeclaration == null ? object.getClass() : classForDeclaration;
+      Method declaredMethod = clazz.getDeclaredMethod(declaredMember);
+      modifier.accept(declaredMethod);
+      // The reflection helper do not support arguments at this point.
+      Object returnValue = declaredMethod.invoke(object);
+      return nextOperation != null ? nextOperation.compute(returnValue) : returnValue;
+    }
+  }
+
+  public static class ReflectiveFieldOperation extends ReflectiveOperation<Field> {
+
+    private ReflectiveFieldOperation(
+        Class<?> classForDeclaration,
+        String declaredMember,
+        ReflectiveOperation<?> nextOperation,
+        Consumer<Field> modifier) {
+      super(classForDeclaration, declaredMember, nextOperation, modifier);
+    }
+
+    @Override
+    public Object compute(Object object) throws Exception {
+      Class<?> clazz = classForDeclaration == null ? object.getClass() : classForDeclaration;
+      Field declaredField = clazz.getDeclaredField(declaredMember);
+      modifier.accept(declaredField);
+      Object fieldValue = declaredField.get(object);
+      return nextOperation != null ? nextOperation.compute(fieldValue) : fieldValue;
+    }
+  }
+
+  public static class ReflectiveOperationSequenceBuilder {
+
+    List<ReflectiveOperationBuilder> reflectiveOperationBuilderList = new ArrayList<>();
+
+    public ReflectiveOperationBuilder readMethod(String declaredMember) {
+      return add(declaredMember, DeclaredType.METHOD);
+    }
+
+    public ReflectiveOperationBuilder readField(String declaredMember) {
+      return add(declaredMember, DeclaredType.FIELD);
+    }
+
+    private ReflectiveOperationBuilder add(String declaredMember, DeclaredType declaredType) {
+      ReflectiveOperationBuilder reflectiveOperationBuilder =
+          new ReflectiveOperationBuilder(declaredMember, declaredType, this);
+      reflectiveOperationBuilderList.add(reflectiveOperationBuilder);
+      return reflectiveOperationBuilder;
+    }
+
+    public ReflectiveOperation<?> build() {
+      assert !reflectiveOperationBuilderList.isEmpty();
+      ReflectiveOperation<?> lastOperation = null;
+      for (int i = reflectiveOperationBuilderList.size() - 1; i >= 0; i--) {
+        lastOperation = reflectiveOperationBuilderList.get(i).build(lastOperation);
+      }
+      return lastOperation;
+    }
+  }
+
+  public static class ReflectiveOperationBuilder {
+
+    private final String declaredMember;
+    private final DeclaredType declaredType;
+    private boolean setAccessible = false;
+    private final ReflectiveOperationSequenceBuilder sequenceBuilder;
+
+    private ReflectiveOperationBuilder(
+        String declaredMember,
+        DeclaredType declaredType,
+        ReflectiveOperationSequenceBuilder sequenceBuilder) {
+      this.declaredMember = declaredMember;
+      this.declaredType = declaredType;
+      this.sequenceBuilder = sequenceBuilder;
+    }
+
+    public ReflectiveOperationBuilder setSetAccessible(boolean setAccessible) {
+      this.setAccessible = setAccessible;
+      return this;
+    }
+
+    public ReflectiveOperationSequenceBuilder done() {
+      return sequenceBuilder;
+    }
+
+    private ReflectiveOperation<?> build(ReflectiveOperation<?> nextOperation) {
+      if (declaredType == DeclaredType.FIELD) {
+        return new ReflectiveFieldOperation(
+            null,
+            declaredMember,
+            nextOperation,
+            field -> {
+              if (setAccessible) {
+                field.setAccessible(true);
+              }
+            });
+      } else {
+        assert declaredType == DeclaredType.METHOD;
+        return new ReflectiveMethodOperation(
+            null,
+            declaredMember,
+            nextOperation,
+            method -> {
+              if (setAccessible) {
+                method.setAccessible(true);
+              }
+            });
+      }
+    }
+  }
+}
diff --git a/src/main/keep.txt b/src/main/keep.txt
index f3e6dd4..e03ef61 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -29,3 +29,20 @@
 
 # TODO(b/176783536): Avoid need to use -dontwarn.
 -include dontwarn.txt
+
+# TODO(b/185756596): Remove when no longer needed
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$FileFacade {
+  com.android.tools.r8.jetbrains.kotlin.Lazy packageData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$Class {
+  com.android.tools.r8.jetbrains.kotlin.Lazy classData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$SyntheticClass {
+  com.android.tools.r8.jetbrains.kotlin.Lazy functionData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlinx.metadata.jvm.KotlinClassMetadata$MultiFileClassPart {
+  com.android.tools.r8.jetbrains.kotlin.Lazy packageData$delegate;
+}
+-keep class com.android.tools.r8.jetbrains.kotlin.SafePublicationLazyImpl {
+  java.lang.Object getValue();
+}
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 7f59069..11ad201 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -94,7 +94,10 @@
             .compile()
             .inspect(
                 inspector ->
-                    assertEqualMetadata(new CodeInspector(BASE_LIBRARY), inspector, i -> {}))
+                    assertEqualMetadata(
+                        new CodeInspector(BASE_LIBRARY),
+                        inspector,
+                        (addedStrings, addedNonInitStrings) -> {}))
             .writeToZip();
     Path testJar = compileTestSources(baseJar);
     runTestsInJar(testJar, baseJar);
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 8178717..996dc79 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -3,11 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
+import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertNotNull;
 import static org.hamcrest.CoreMatchers.anyOf;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
 import com.android.tools.r8.KotlinTestBase;
@@ -19,9 +21,15 @@
 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.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
-import java.util.function.IntConsumer;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
 import junit.framework.TestCase;
 import kotlinx.metadata.jvm.KotlinClassHeader;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -35,7 +43,6 @@
   static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
   static final String PKG_PREFIX = DescriptorUtils.getBinaryNameFromJavaType(PKG);
 
-  static final String KT_ANY = "Lkotlin/Any;";
   static final String KT_ARRAY = "Lkotlin/Array;";
   static final String KT_CHAR_SEQUENCE = "Lkotlin/CharSequence;";
   static final String KT_STRING = "Lkotlin/String;";
@@ -50,9 +57,13 @@
   public void assertEqualMetadata(
       CodeInspector originalInspector,
       CodeInspector rewrittenInspector,
-      IntConsumer addedStringsInspector) {
+      BiConsumer<Integer, Integer> addedStringsInspector) {
     IntBox addedStrings = new IntBox();
-    for (FoundClassSubject clazzSubject : originalInspector.allClasses()) {
+    IntBox addedNonInitStrings = new IntBox();
+    for (FoundClassSubject clazzSubject :
+        originalInspector.allClasses().stream()
+            .sorted(Comparator.comparing(FoundClassSubject::getFinalName))
+            .collect(Collectors.toList())) {
       ClassSubject r8Clazz = rewrittenInspector.clazz(clazzSubject.getOriginalName());
       assertThat(r8Clazz, isPresent());
       KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
@@ -65,9 +76,17 @@
       KotlinClassHeader originalHeader = originalMetadata.getHeader();
       KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
       TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
-      // TODO(b/154199572): Should we check for meta-data version?
-      TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
 
+      // 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
+      // by using the KotlinMetadataWriter.
+      Map<String, List<String>> descriptorToNames = new HashMap<>();
+      clazzSubject.forAllMethods(
+          method ->
+              descriptorToNames
+                  .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()));
       rewrittenStrings.forEach(
@@ -76,14 +95,28 @@
               return;
             }
             addedStrings.increment();
+            // The init is not needed by if we cannot lookup the descriptor in the table, we have
+            // to emit it and that adds <init>.
+            if (rewrittenString.equals("<init>")) {
+              return;
+            }
+            // We have decided to keep invalid signatures, but they will end up in the string pool
+            // when we emit them. The likely cause of them not being there in the first place seems
+            // to be that they are not correctly written in the type table.
+            if (rewrittenString.equals("L;") || rewrittenString.equals("(L;)V")) {
+              return;
+            }
+            System.out.println(clazzSubject.toString() + ": " + rewrittenString);
+            addedNonInitStrings.increment();
           });
-      // We cannot assert equality of the data since it may be ordered differently. Instead we use
-      // the KotlinMetadataWriter.
+      System.out.flush();
+      assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+
       String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
       String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
-      TestCase.assertEquals(expected, actual);
+      assertEquals(expected, actual);
     }
-    addedStringsInspector.accept(addedStrings.get());
+    addedStringsInspector.accept(addedStrings.get(), addedNonInitStrings.get());
   }
 
   public static void verifyExpectedWarningsFromKotlinReflectAndStdLib(
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index 12c1934..ffe8b3a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -77,7 +77,7 @@
                     assertEqualMetadata(
                         new CodeInspector(jars.getForConfiguration(kotlinc, targetVersion)),
                         inspector,
-                        i -> {}))
+                        (addedStrings, addedNonInitStrings) -> {}))
             .writeToZip();
     testForJvm()
         .addRunClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinReflectJar(kotlinc))
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 4da59e8..39b540f 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
@@ -40,11 +40,23 @@
 
   public int getExpectedAddedCount() {
     if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_3_72)) {
-      return 2441;
+      return 597;
     } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_4_20)) {
-      return 2561;
+      return 685;
     } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_5_0_M2)) {
-      return 2594;
+      return 694;
+    } else {
+      throw new Unreachable("Should not compile in this configuration");
+    }
+  }
+
+  public int getExpectedNonInitAddedCount() {
+    if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_3_72)) {
+      return 327;
+    } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_4_20)) {
+      return 413;
+    } else if (kotlinParameters.getCompiler().is(KotlinCompilerVersion.KOTLINC_1_5_0_M2)) {
+      return 417;
     } else {
       throw new Unreachable("Should not compile in this configuration");
     }
@@ -66,8 +78,9 @@
                 assertEqualMetadata(
                     new CodeInspector(getKotlinStdlibJar(kotlinc)),
                     inspector,
-                    addedStrings -> {
-                      assertEquals(getExpectedAddedCount(), addedStrings);
+                    (addedStrings, addedNonInitStrings) -> {
+                      assertEquals(getExpectedAddedCount(), addedStrings.intValue());
+                      assertEquals(getExpectedNonInitAddedCount(), addedNonInitStrings.intValue());
                     }));
   }
 }