Reland "Check shape and class type before parsing metadata for lambdas"

This reverts commit b1c97136907e130b6756db143c0f50e6f6b273f2.

Change-Id: Ic95a04869c9e07e6aec74a81c6ebade4fe16620e
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 bfabd6c..7f4c47d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -33,6 +33,8 @@
 
 public final class KotlinClassMetadataReader {
 
+  private static final int SYNTHETIC_CLASS_KIND = 3;
+
   public static KotlinClassLevelInfo getKotlinInfo(
       DexClass clazz, AppView<?> appView, Consumer<DexEncodedMethod> keepByteCode) {
     DexAnnotation meta =
@@ -75,16 +77,25 @@
   public static boolean isLambda(AppView<?> appView, DexClass clazz) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     Kotlin kotlin = dexItemFactory.kotlin;
+    Flavour flavour = getFlavour(clazz, kotlin);
+    if (flavour == Flavour.Unclassified) {
+      return false;
+    }
     DexAnnotation metadataAnnotation =
         clazz.annotations().getFirstMatching(dexItemFactory.kotlinMetadataType);
-    if (metadataAnnotation != null) {
-      KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, metadataAnnotation.annotation);
+    if (metadataAnnotation == null) {
+      return false;
+    }
+    Map<DexString, DexAnnotationElement> elementMap = toElementMap(metadataAnnotation.annotation);
+    if (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND) {
+      KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, elementMap);
       if (kMetadata instanceof SyntheticClass) {
-        SyntheticClass syntheticClass = (SyntheticClass) kMetadata;
-        return syntheticClass.isLambda()
-            && getFlavour(syntheticClass, clazz, kotlin) != Flavour.Unclassified;
+        return ((SyntheticClass) kMetadata).isLambda();
       }
     }
+    assert toKotlinClassMetadata(kotlin, elementMap) instanceof SyntheticClass
+            == (getKind(kotlin, elementMap) == SYNTHETIC_CLASS_KIND)
+        : "Synthetic class kinds should agree";
     return false;
   }
 
@@ -98,16 +109,21 @@
 
   public static KotlinClassMetadata toKotlinClassMetadata(
       Kotlin kotlin, DexEncodedAnnotation metadataAnnotation) {
+    return toKotlinClassMetadata(kotlin, toElementMap(metadataAnnotation));
+  }
+
+  private static Map<DexString, DexAnnotationElement> toElementMap(
+      DexEncodedAnnotation metadataAnnotation) {
     Map<DexString, DexAnnotationElement> elementMap = new IdentityHashMap<>();
     for (DexAnnotationElement element : metadataAnnotation.elements) {
       elementMap.put(element.name, element);
     }
+    return elementMap;
+  }
 
-    DexAnnotationElement kind = elementMap.get(kotlin.metadata.kind);
-    if (kind == null) {
-      throw new MetadataError("element 'k' is missing.");
-    }
-    Integer k = (Integer) kind.value.getBoxedValue();
+  private static KotlinClassMetadata toKotlinClassMetadata(
+      Kotlin kotlin, Map<DexString, DexAnnotationElement> elementMap) {
+    int k = getKind(kotlin, elementMap);
     DexAnnotationElement metadataVersion = elementMap.get(kotlin.metadata.metadataVersion);
     int[] mv = metadataVersion == null ? null : getUnboxedIntArray(metadataVersion.value, "mv");
     DexAnnotationElement bytecodeVersion = elementMap.get(kotlin.metadata.bytecodeVersion);
@@ -127,6 +143,14 @@
     return KotlinClassMetadata.read(header);
   }
 
+  private static int getKind(Kotlin kotlin, Map<DexString, DexAnnotationElement> elementMap) {
+    DexAnnotationElement kind = elementMap.get(kotlin.metadata.kind);
+    if (kind == null) {
+      throw new MetadataError("element 'k' is missing.");
+    }
+    return (Integer) kind.value.getBoxedValue();
+  }
+
   public static KotlinClassLevelInfo createKotlinInfo(
       Kotlin kotlin,
       DexClass clazz,
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 08e95b3..ee55f8b 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.utils.Pair;
 import kotlinx.metadata.KmLambda;
 import kotlinx.metadata.jvm.KotlinClassHeader;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
 import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
 import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass.Writer;
 
@@ -55,7 +54,7 @@
             ? KotlinLambdaInfo.create(
                 clazz, lambda, appView.dexItemFactory(), appView.reporter(), extensionInformation)
             : null,
-        getFlavour(syntheticClass, clazz, kotlin),
+        getFlavour(clazz, kotlin),
         packageName,
         metadataVersion);
   }
@@ -64,14 +63,6 @@
     return lambda != null && flavour != Flavour.Unclassified;
   }
 
-  public boolean isKotlinStyleLambda() {
-    return flavour == Flavour.KotlinStyleLambda;
-  }
-
-  public boolean isJavaStyleLambda() {
-    return flavour == Flavour.JavaStyleLambda;
-  }
-
   @Override
   public boolean isSyntheticClass() {
     return true;
@@ -112,23 +103,17 @@
     return metadataVersion;
   }
 
-  public static Flavour getFlavour(
-      KotlinClassMetadata.SyntheticClass metadata, DexClass clazz, Kotlin kotlin) {
-    // Returns KotlinStyleLambda if the given clazz is a Kotlin-style lambda:
-    //   a class that
-    //     1) is recognized as lambda in its Kotlin metadata;
-    //     2) directly extends kotlin.jvm.internal.Lambda
-    if (metadata.isLambda() && clazz.superType == kotlin.functional.lambdaType) {
+  public static Flavour getFlavour(DexClass clazz, Kotlin kotlin) {
+    // Returns KotlinStyleLambda if the given clazz has shape of a Kotlin-style lambda:
+    //   a class that directly extends kotlin.jvm.internal.Lambda
+    if (clazz.superType == kotlin.functional.lambdaType) {
       return Flavour.KotlinStyleLambda;
     }
-    // Returns JavaStyleLambda if the given clazz is a Java-style lambda:
+    // Returns JavaStyleLambda if the given clazz has shape of a Java-style lambda:
     //  a class that
-    //    1) is recognized as lambda in its Kotlin metadata;
-    //    2) doesn't extend any other class;
-    //    3) directly implements only one Java SAM.
-    if (metadata.isLambda()
-        && clazz.superType == kotlin.factory.objectType
-        && clazz.interfaces.size() == 1) {
+    //    1) doesn't extend any other class;
+    //    2) directly implements only one Java SAM.
+    if (clazz.superType == kotlin.factory.objectType && clazz.interfaces.size() == 1) {
       return Flavour.JavaStyleLambda;
     }
     return Flavour.Unclassified;