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;
+ }
+
}