Merge "Reland "From DexAnnotation to KotlinInfo   via kotlinx.metadata.jvm.KotlinClassMetadata""
diff --git a/build.gradle b/build.gradle
index 92d7471..7f41caa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -37,6 +37,7 @@
     gsonVersion = '2.7'
     junitVersion = '4.12'
     kotlinVersion = '1.2.30'
+    kotlinExtMetadataJVMVersion = '0.0.2'
     protobufVersion = '3.0.0'
     smaliVersion = '2.2b4'
 }
@@ -74,6 +75,7 @@
 
 repositories {
     maven { url 'https://maven.google.com' }
+    maven { url 'https://kotlin.bintray.com/kotlinx' }
     mavenCentral()
 }
 
@@ -223,6 +225,7 @@
         exclude group: 'org.codehaus.mojo'
     })
     compile group: 'it.unimi.dsi', name: 'fastutil', version: fastutilVersion
+    compile "org.jetbrains.kotlinx:kotlinx-metadata-jvm:$kotlinExtMetadataJVMVersion"
     compile group: 'org.ow2.asm', name: 'asm', version: asmVersion
     compile group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion
     compile group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index cf2b2bc..0af41b1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -5,19 +5,11 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueArray;
-import com.android.tools.r8.graph.DexValue.DexValueInt;
-import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.kotlin.KotlinSyntheticClass.Flavour;
-import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
 import java.util.Set;
 
@@ -58,10 +50,13 @@
 
     public final DexString kotlinStyleLambdaInstanceName = factory.createString("INSTANCE");
 
+    public final DexType functionBase = factory.createType("Lkotlin/jvm/internal/FunctionBase;");
     public final DexType lambdaType = factory.createType("Lkotlin/jvm/internal/Lambda;");
 
-    public final DexMethod lambdaInitializerMethod = factory.createMethod(lambdaType,
-        factory.createProto(factory.voidType, factory.intType), factory.constructorMethodName);
+    public final DexMethod lambdaInitializerMethod = factory.createMethod(
+        lambdaType,
+        factory.createProto(factory.voidType, factory.intType),
+        factory.constructorMethodName);
 
     public boolean isFunctionInterface(DexType type) {
       return functions.contains(type);
@@ -70,9 +65,14 @@
 
   public final class Metadata {
     public final DexType kotlinMetadataType = factory.createType("Lkotlin/Metadata;");
-    public final DexString elementNameK = factory.createString("k");
-    public final DexString elementNameD1 = factory.createString("d1");
-    public final DexString elementNameD2 = factory.createString("d2");
+    public final DexString kind = factory.createString("k");
+    public final DexString metadataVersion = factory.createString("mv");
+    public final DexString bytecodeVersion = factory.createString("bv");
+    public final DexString data1 = factory.createString("d1");
+    public final DexString data2 = factory.createString("d2");
+    public final DexString extraString = factory.createString("xs");
+    public final DexString packageName = factory.createString("pn");
+    public final DexString extraInt = factory.createString("xi");
   }
 
   // kotlin.jvm.internal.Intrinsics class
@@ -86,98 +86,6 @@
 
   // Calculates kotlin info for a class.
   public KotlinInfo getKotlinInfo(DexClass clazz, DiagnosticsHandler reporter) {
-    if (clazz.annotations.isEmpty()) {
-      return null;
-    }
-    DexAnnotation meta = clazz.annotations.getFirstMatching(metadata.kotlinMetadataType);
-    if (meta != null) {
-      try {
-        return createKotlinInfo(clazz, meta);
-      } catch (MetadataError e) {
-        reporter.warning(
-            new StringDiagnostic("Class " + clazz.type.toSourceString() +
-                " has malformed kotlin.Metadata: " + e.getMessage()));
-      }
-    }
-    return null;
-  }
-
-  private KotlinInfo createKotlinInfo(DexClass clazz, DexAnnotation meta) {
-    DexAnnotationElement kindElement = getAnnotationElement(meta, metadata.elementNameK);
-    if (kindElement == null) {
-      throw new MetadataError("element 'k' is missing");
-    }
-
-    DexValue value = kindElement.value;
-    if (!(value instanceof DexValueInt)) {
-      throw new MetadataError("invalid 'k' value: " + value.toSourceString());
-    }
-
-    DexValueInt intValue = (DexValueInt) value;
-    switch (intValue.value) {
-      case 1:
-        return new KotlinClass();
-      case 2:
-        return new KotlinFile();
-      case 3:
-        return createSyntheticClass(clazz, meta);
-      case 4:
-        return new KotlinClassFacade();
-      case 5:
-        return new KotlinClassPart();
-      default:
-        throw new MetadataError("unsupported 'k' value: " + value.toSourceString());
-    }
-  }
-
-  private KotlinSyntheticClass createSyntheticClass(DexClass clazz, DexAnnotation meta) {
-    if (isKotlinStyleLambda(clazz)) {
-      return new KotlinSyntheticClass(Flavour.KotlinStyleLambda);
-    }
-    if (isJavaStyleLambda(clazz, meta)) {
-      return new KotlinSyntheticClass(Flavour.JavaStyleLambda);
-    }
-    return new KotlinSyntheticClass(Flavour.Unclassified);
-  }
-
-  private boolean isKotlinStyleLambda(DexClass clazz) {
-    // TODO: replace with direct hints from kotlin metadata when available.
-    return clazz.superType == this.functional.lambdaType;
-  }
-
-  private boolean isJavaStyleLambda(DexClass clazz, DexAnnotation meta) {
-    assert !isKotlinStyleLambda(clazz);
-    return clazz.superType == this.factory.objectType &&
-        clazz.interfaces.size() == 1 &&
-        isAnnotationElementNotEmpty(meta, metadata.elementNameD1);
-  }
-
-  private DexAnnotationElement getAnnotationElement(DexAnnotation annotation, DexString name) {
-    for (DexAnnotationElement element : annotation.annotation.elements) {
-      if (element.name == name) {
-        return element;
-      }
-    }
-    return null;
-  }
-
-  private boolean isAnnotationElementNotEmpty(DexAnnotation annotation, DexString name) {
-    for (DexAnnotationElement element : annotation.annotation.elements) {
-      if (element.name == name && element.value instanceof DexValueArray) {
-        DexValue[] values = ((DexValueArray) element.value).getValues();
-        if (values.length == 1 && values[0] instanceof DexValueString) {
-          return true;
-        }
-        // Must be broken metadata.
-        assert false;
-      }
-    }
-    return false;
-  }
-
-  private static class MetadataError extends RuntimeException {
-    MetadataError(String cause) {
-      super(cause);
-    }
+    return KotlinClassMetadataReader.getKotlinInfo(this, clazz, reporter);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index fc81994..74184a5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -4,7 +4,31 @@
 
 package com.android.tools.r8.kotlin;
 
-public class KotlinClass extends KotlinInfo {
+import kotlinx.metadata.KmClassVisitor;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public class KotlinClass extends KotlinInfo<KotlinClassMetadata.Class> {
+
+  static KotlinClass fromKotlinClassMetadata(KotlinClassMetadata kotlinClassMetadata) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.Class;
+    KotlinClassMetadata.Class kClass = (KotlinClassMetadata.Class) kotlinClassMetadata;
+    return new KotlinClass(kClass);
+  }
+
+  private KotlinClass(KotlinClassMetadata.Class metadata) {
+    super(metadata);
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.Class metadata) {
+    ClassMetadataVisitor visitor = new ClassMetadataVisitor();
+    // To avoid lazy parsing/verifying metadata.
+    metadata.accept(visitor);
+  }
+
+  private static class ClassMetadataVisitor extends KmClassVisitor {
+  }
+
   @Override
   public Kind getKind() {
     return Kind.Class;
@@ -20,6 +44,4 @@
     return this;
   }
 
-  KotlinClass() {
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
index e829a27..62db3d1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
@@ -4,7 +4,26 @@
 
 package com.android.tools.r8.kotlin;
 
-public final class KotlinClassFacade extends KotlinInfo {
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public final class KotlinClassFacade extends KotlinInfo<KotlinClassMetadata.MultiFileClassFacade> {
+
+  static KotlinClassFacade fromKotlinClassMetadata(KotlinClassMetadata kotlinClassMetadata) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.MultiFileClassFacade;
+    KotlinClassMetadata.MultiFileClassFacade multiFileClassFacade =
+        (KotlinClassMetadata.MultiFileClassFacade) kotlinClassMetadata;
+    return new KotlinClassFacade(multiFileClassFacade);
+  }
+
+  private KotlinClassFacade(KotlinClassMetadata.MultiFileClassFacade metadata) {
+    super(metadata);
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.MultiFileClassFacade metadata) {
+    // No worries about lazy parsing/verifying, since no API to explore metadata details.
+  }
+
   @Override
   public Kind getKind() {
     return Kind.Facade;
@@ -20,7 +39,4 @@
     return this;
   }
 
-  KotlinClassFacade() {
-    super();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
new file mode 100644
index 0000000..32aebff
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2018, 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.DiagnosticsHandler;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import kotlinx.metadata.InconsistentKotlinMetadataException;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+final class KotlinClassMetadataReader {
+
+  static KotlinInfo getKotlinInfo(
+      Kotlin kotlin,
+      DexClass clazz,
+      DiagnosticsHandler reporter) {
+    if (clazz.annotations.isEmpty()) {
+      return null;
+    }
+    DexAnnotation meta = clazz.annotations.getFirstMatching(kotlin.metadata.kotlinMetadataType);
+    if (meta != null) {
+      try {
+        return createKotlinInfo(kotlin, clazz, meta);
+      } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
+        reporter.warning(
+            new StringDiagnostic("Class " + clazz.type.toSourceString()
+                + " has malformed kotlin.Metadata: " + e.getMessage()));
+      } catch (Throwable e) {
+         reporter.warning(
+            new StringDiagnostic("Unexpected error while reading " + clazz.type.toSourceString()
+                + "'s kotlin.Metadata: " + e.getMessage()));
+      }
+    }
+    return null;
+  }
+
+  private static KotlinInfo createKotlinInfo(
+      Kotlin kotlin,
+      DexClass clazz,
+      DexAnnotation meta) {
+    Map<DexString, DexAnnotationElement> elementMap = new IdentityHashMap<>();
+    for (DexAnnotationElement element : meta.annotation.elements) {
+      elementMap.put(element.name, element);
+    }
+
+    DexAnnotationElement kind = elementMap.get(kotlin.metadata.kind);
+    if (kind == null) {
+      throw new MetadataError("element 'k' is missing.");
+    }
+    Integer k = (Integer) kind.value.getBoxedValue();
+    DexAnnotationElement metadataVersion = elementMap.get(kotlin.metadata.metadataVersion);
+    int[] mv = metadataVersion == null ? null : getUnboxedIntArray(metadataVersion.value, "mv");
+    DexAnnotationElement bytecodeVersion = elementMap.get(kotlin.metadata.bytecodeVersion);
+    int[] bv = bytecodeVersion == null ? null : getUnboxedIntArray(bytecodeVersion.value, "bv");
+    DexAnnotationElement data1 = elementMap.get(kotlin.metadata.data1);
+    String[] d1 = data1 == null ? null : getUnboxedStringArray(data1.value, "d1");
+    DexAnnotationElement data2 = elementMap.get(kotlin.metadata.data2);
+    String[] d2 = data2 == null ? null : getUnboxedStringArray(data2.value, "d2");
+    DexAnnotationElement extraString = elementMap.get(kotlin.metadata.extraString);
+    String xs = extraString == null ? null : getUnboxedString(extraString.value, "xs");
+    DexAnnotationElement packageName = elementMap.get(kotlin.metadata.packageName);
+    String pn = packageName == null ? null : getUnboxedString(packageName.value, "pn");
+    DexAnnotationElement extraInt = elementMap.get(kotlin.metadata.extraInt);
+    Integer xi = extraInt == null ? null : (Integer) extraInt.value.getBoxedValue();
+
+    KotlinClassHeader header = new KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi);
+    KotlinClassMetadata kMetadata = KotlinClassMetadata.read(header);
+
+    if (kMetadata instanceof KotlinClassMetadata.Class) {
+      return KotlinClass.fromKotlinClassMetadata(kMetadata);
+    } else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
+      return KotlinFile.fromKotlinClassMetadata(kMetadata);
+    } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
+      return KotlinClassFacade.fromKotlinClassMetadata(kMetadata);
+    } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
+      return KotlinClassPart.fromKotlinClassMetdata(kMetadata);
+    } else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
+      return KotlinSyntheticClass.fromKotlinClassMetadata(kMetadata, kotlin, clazz);
+    } else {
+      throw new MetadataError("unsupported 'k' value: " + k);
+    }
+  }
+
+  private static int[] getUnboxedIntArray(DexValue v, String elementName) {
+    if (!(v instanceof DexValueArray)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    DexValueArray intArrayValue = (DexValueArray) v;
+    DexValue[] values = intArrayValue.getValues();
+    int[] result = new int [values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = (Integer) values[i].getBoxedValue();
+    }
+    return result;
+  }
+
+  private static String[] getUnboxedStringArray(DexValue v, String elementName) {
+    if (!(v instanceof DexValueArray)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    DexValueArray stringArrayValue = (DexValueArray) v;
+    DexValue[] values = stringArrayValue.getValues();
+    String[] result = new String [values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = getUnboxedString(values[i], elementName + "[" + i + "]");
+    }
+    return result;
+  }
+
+  private static String getUnboxedString(DexValue v, String elementName) {
+    if (!(v instanceof DexValueString)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    return ((DexValueString) v).getValue().toString();
+  }
+
+  private static class MetadataError extends RuntimeException {
+    MetadataError(String cause) {
+      super(cause);
+    }
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index d6da817..da66e6c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -4,7 +4,31 @@
 
 package com.android.tools.r8.kotlin;
 
-public final class KotlinClassPart extends KotlinInfo {
+import kotlinx.metadata.KmPackageVisitor;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public final class KotlinClassPart extends KotlinInfo<KotlinClassMetadata.MultiFileClassPart> {
+
+  static KotlinClassPart fromKotlinClassMetdata(KotlinClassMetadata kotlinClassMetadata) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.MultiFileClassPart;
+    KotlinClassMetadata.MultiFileClassPart multiFileClassPart =
+        (KotlinClassMetadata.MultiFileClassPart) kotlinClassMetadata;
+    return new KotlinClassPart(multiFileClassPart);
+  }
+
+  private KotlinClassPart(KotlinClassMetadata.MultiFileClassPart metadata) {
+    super(metadata);
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.MultiFileClassPart metadata) {
+    // To avoid lazy parsing/verifying metadata.
+    metadata.accept(new MultiFileClassPartMetadataVisitor());
+  }
+
+  private static class MultiFileClassPartMetadataVisitor extends KmPackageVisitor {
+  }
+
   @Override
   public Kind getKind() {
     return Kind.Part;
@@ -20,6 +44,4 @@
     return this;
   }
 
-  KotlinClassPart() {
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index 38a77ad..bcb70ed 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -4,7 +4,31 @@
 
 package com.android.tools.r8.kotlin;
 
-public final class KotlinFile extends KotlinInfo {
+import kotlinx.metadata.KmPackageVisitor;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public final class KotlinFile extends KotlinInfo<KotlinClassMetadata.FileFacade> {
+
+  static KotlinFile fromKotlinClassMetadata(KotlinClassMetadata kotlinClassMetadata) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.FileFacade;
+    KotlinClassMetadata.FileFacade fileFacade =
+        (KotlinClassMetadata.FileFacade) kotlinClassMetadata;
+    return new KotlinFile(fileFacade);
+  }
+
+  private KotlinFile(KotlinClassMetadata.FileFacade metadata) {
+    super(metadata);
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.FileFacade metadata) {
+    // To avoid lazy parsing/verifying metadata.
+    metadata.accept(new FileFacadeMetadataVisitor());
+  }
+
+  private static class FileFacadeMetadataVisitor extends KmPackageVisitor {
+  }
+
   @Override
   public Kind getKind() {
     return Kind.File;
@@ -20,6 +44,4 @@
     return this;
   }
 
-  KotlinFile() {
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
index 4043bc6..702e0eb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -4,11 +4,23 @@
 
 package com.android.tools.r8.kotlin;
 
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
 // Provides access to kotlin information.
-public abstract class KotlinInfo {
+public abstract class KotlinInfo<MetadataKind extends KotlinClassMetadata> {
+  MetadataKind metadata;
+
   KotlinInfo() {
   }
 
+  KotlinInfo(MetadataKind metadata) {
+    validateMetadata(metadata);
+    this.metadata = metadata;
+  }
+
+  // Subtypes will define how to validate the given metadata.
+  abstract void validateMetadata(MetadataKind metadata);
+
   public enum Kind {
     Class, File, Synthetic, Part, Facade
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
index 68721bb..4660800 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
@@ -4,7 +4,11 @@
 
 package com.android.tools.r8.kotlin;
 
-public final class KotlinSyntheticClass extends KotlinInfo {
+import com.android.tools.r8.graph.DexClass;
+import kotlinx.metadata.KmLambdaVisitor;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public final class KotlinSyntheticClass extends KotlinInfo<KotlinClassMetadata.SyntheticClass> {
   public enum Flavour {
     KotlinStyleLambda,
     JavaStyleLambda,
@@ -13,12 +17,40 @@
 
   private final Flavour flavour;
 
-  KotlinSyntheticClass(Flavour flavour) {
+  static KotlinSyntheticClass fromKotlinClassMetadata(
+      KotlinClassMetadata kotlinClassMetadata, Kotlin kotlin, DexClass clazz) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.SyntheticClass;
+    KotlinClassMetadata.SyntheticClass syntheticClass =
+        (KotlinClassMetadata.SyntheticClass) kotlinClassMetadata;
+    if (isKotlinStyleLambda(syntheticClass, kotlin, clazz)) {
+      return new KotlinSyntheticClass(Flavour.KotlinStyleLambda, syntheticClass);
+    } else if (isJavaStyleLambda(syntheticClass, kotlin, clazz)) {
+      return new KotlinSyntheticClass(Flavour.JavaStyleLambda, syntheticClass);
+    } else {
+      return new KotlinSyntheticClass(Flavour.Unclassified, syntheticClass);
+    }
+  }
+
+  private KotlinSyntheticClass(Flavour flavour, KotlinClassMetadata.SyntheticClass metadata) {
     this.flavour = flavour;
+    validateMetadata(metadata);
+    this.metadata = metadata;
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.SyntheticClass metadata) {
+    if (metadata.isLambda()) {
+      SyntheticClassMetadataVisitor visitor = new SyntheticClassMetadataVisitor();
+      // To avoid lazy parsing/verifying metadata.
+      metadata.accept(visitor);
+    }
+  }
+
+  private static class SyntheticClassMetadataVisitor extends KmLambdaVisitor {
   }
 
   public boolean isLambda() {
-    return flavour == Flavour.KotlinStyleLambda || flavour == Flavour.JavaStyleLambda;
+    return isKotlinStyleLambda() || isJavaStyleLambda();
   }
 
   public boolean isKotlinStyleLambda() {
@@ -43,4 +75,31 @@
   public KotlinSyntheticClass asSyntheticClass() {
     return this;
   }
+
+  /**
+   * Returns {@code true} if the given {@link DexClass} is a Kotlin-style lambda:
+   *   a class that
+   *     1) is recognized as lambda in its Kotlin metadata;
+   *     2) directly extends kotlin.jvm.internal.Lambda
+   */
+  private static boolean isKotlinStyleLambda(
+      KotlinClassMetadata.SyntheticClass metadata, Kotlin kotlin, DexClass clazz) {
+    return metadata.isLambda()
+        && clazz.superType == kotlin.functional.lambdaType;
+  }
+
+  /**
+   * Returns {@code true} if the given {@link DexClass} is 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.
+   */
+  private static boolean isJavaStyleLambda(
+      KotlinClassMetadata.SyntheticClass metadata, Kotlin kotlin, DexClass clazz) {
+    return metadata.isLambda()
+        && clazz.superType == kotlin.factory.objectType
+        && clazz.interfaces.size() == 1;
+  }
+
 }