blob: 8dfe7612d4f80b346f3931cdf6809bb2149f07ee [file] [log] [blame]
// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import kotlinx.metadata.InconsistentKotlinMetadataException;
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;
// Holds information about a Metadata.SyntheticClass object.
public class KotlinSyntheticClassInfo implements KotlinClassLevelInfo {
private final KotlinLambdaInfo lambda;
public enum Flavour {
KotlinStyleLambda,
JavaStyleLambda,
Unclassified
}
private final Flavour flavour;
private KotlinSyntheticClassInfo(KotlinLambdaInfo lambda, Flavour flavour) {
this.lambda = lambda;
this.flavour = flavour;
}
static KotlinSyntheticClassInfo create(
SyntheticClass syntheticClass, DexClass clazz, Kotlin kotlin, AppView<?> appView) {
KmLambda lambda = null;
if (syntheticClass.isLambda()) {
try {
lambda = syntheticClass.toKmLambda();
assert lambda != null;
} catch (InconsistentKotlinMetadataException ex) {
// TODO(b/155534905): Gracefully handle these errors by retaining the original object.
}
}
return new KotlinSyntheticClassInfo(
lambda != null ? KotlinLambdaInfo.create(clazz, lambda, appView) : null,
getFlavour(syntheticClass, clazz, kotlin));
}
public boolean isLambda() {
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;
}
@Override
public KotlinSyntheticClassInfo asSyntheticClass() {
return this;
}
@Override
public KotlinClassHeader rewrite(
DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
Writer writer = new Writer();
if (lambda != null) {
KmLambda kmLambda = new KmLambda();
if (lambda.rewrite(() -> kmLambda, clazz, appView, namingLens)) {
kmLambda.accept(writer);
}
}
return writer.write().getHeader();
}
private 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) {
return Flavour.KotlinStyleLambda;
}
// Returns JavaStyleLambda if the given clazz 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.
if (metadata.isLambda()
&& clazz.superType == kotlin.factory.objectType
&& clazz.interfaces.size() == 1) {
return Flavour.JavaStyleLambda;
}
return Flavour.Unclassified;
}
}