blob: 79054f442241555863c80ec5c9ae15f58dfe01e8 [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.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.Reporter;
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;
private final String packageName;
public enum Flavour {
KotlinStyleLambda,
JavaStyleLambda,
Unclassified
}
private final Flavour flavour;
private KotlinSyntheticClassInfo(KotlinLambdaInfo lambda, Flavour flavour, String packageName) {
this.lambda = lambda;
this.flavour = flavour;
this.packageName = packageName;
}
static KotlinSyntheticClassInfo create(
SyntheticClass syntheticClass,
String packageName,
DexClass clazz,
Kotlin kotlin,
DexItemFactory factory,
Reporter reporter) {
KmLambda lambda = null;
if (syntheticClass.isLambda()) {
lambda = syntheticClass.toKmLambda();
assert lambda != null;
}
return new KotlinSyntheticClassInfo(
lambda != null ? KotlinLambdaInfo.create(clazz, lambda, factory, reporter) : null,
getFlavour(syntheticClass, clazz, kotlin),
packageName);
}
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<?> 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();
}
@Override
public void trace(DexDefinitionSupplier definitionSupplier) {
if (lambda != null) {
lambda.trace(definitionSupplier);
}
}
@Override
public String getPackageName() {
return packageName;
}
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;
}
}