blob: b5070937ac87512628a93d00125e3305013203fc [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 static com.android.tools.r8.kotlin.KotlinMetadataUtils.referenceTypeFromBinaryName;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
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.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import kotlinx.metadata.KmClass;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmType;
import kotlinx.metadata.jvm.JvmExtensionsKt;
import kotlinx.metadata.jvm.JvmMethodSignature;
import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
public class KotlinClassInfo implements KotlinClassLevelInfo {
private final int flags;
private final String name;
private final String moduleName;
private final List<KotlinConstructorInfo> constructorsWithNoBacking;
private final KotlinDeclarationContainerInfo declarationContainerInfo;
private final List<KotlinTypeParameterInfo> typeParameters;
private final List<KotlinTypeInfo> superTypes;
private final List<DexType> sealedSubClasses;
private final List<DexType> nestedClasses;
// TODO(b/154347404): Understand enum entries.
private final List<String> enumEntries;
private final DexType anonymousObjectOrigin;
private final String packageName;
public KotlinClassInfo(
int flags,
String name,
String moduleName,
KotlinDeclarationContainerInfo declarationContainerInfo,
List<KotlinTypeParameterInfo> typeParameters,
List<KotlinConstructorInfo> constructorsWithNoBacking,
List<KotlinTypeInfo> superTypes,
List<DexType> sealedSubClasses,
List<DexType> nestedClasses,
List<String> enumEntries,
DexType anonymousObjectOrigin,
String packageName) {
this.flags = flags;
this.name = name;
this.moduleName = moduleName;
this.declarationContainerInfo = declarationContainerInfo;
this.typeParameters = typeParameters;
this.constructorsWithNoBacking = constructorsWithNoBacking;
this.superTypes = superTypes;
this.sealedSubClasses = sealedSubClasses;
this.nestedClasses = nestedClasses;
this.enumEntries = enumEntries;
this.anonymousObjectOrigin = anonymousObjectOrigin;
this.packageName = packageName;
}
public static KotlinClassInfo create(
KmClass kmClass,
String packageName,
DexClass hostClass,
DexDefinitionSupplier definitionSupplier,
Reporter reporter,
Consumer<DexEncodedMethod> keepByteCode) {
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : hostClass.fields()) {
fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
}
Map<String, DexEncodedMethod> methodMap = new HashMap<>();
for (DexEncodedMethod method : hostClass.methods()) {
methodMap.put(toJvmMethodSignature(method.method).asString(), method);
}
ImmutableList.Builder<KotlinConstructorInfo> notBackedConstructors = ImmutableList.builder();
for (KmConstructor kmConstructor : kmClass.getConstructors()) {
KotlinConstructorInfo constructorInfo =
KotlinConstructorInfo.create(kmConstructor, definitionSupplier, reporter);
JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmConstructor);
if (signature != null) {
DexEncodedMethod method = methodMap.get(signature.asString());
if (method != null) {
method.setKotlinMemberInfo(constructorInfo);
continue;
}
}
// We could not find a definition for the constructor - add it to ensure the same output.
notBackedConstructors.add(constructorInfo);
}
KotlinDeclarationContainerInfo container =
KotlinDeclarationContainerInfo.create(
kmClass, methodMap, fieldMap, definitionSupplier, reporter, keepByteCode);
setCompanionObject(kmClass, hostClass, reporter);
return new KotlinClassInfo(
kmClass.getFlags(),
kmClass.name,
JvmExtensionsKt.getModuleName(kmClass),
container,
KotlinTypeParameterInfo.create(kmClass.getTypeParameters(), definitionSupplier, reporter),
notBackedConstructors.build(),
getSuperTypes(kmClass.getSupertypes(), definitionSupplier, reporter),
getSealedSubClasses(hostClass, kmClass.getSealedSubclasses(), definitionSupplier),
getNestedClasses(hostClass, kmClass.getNestedClasses(), definitionSupplier),
kmClass.getEnumEntries(),
getAnonymousObjectOrigin(kmClass, definitionSupplier),
packageName);
}
private static DexType getAnonymousObjectOrigin(
KmClass kmClass, DexDefinitionSupplier definitionSupplier) {
String anonymousObjectOriginName = JvmExtensionsKt.getAnonymousObjectOriginName(kmClass);
if (anonymousObjectOriginName != null) {
return referenceTypeFromBinaryName(anonymousObjectOriginName, definitionSupplier);
}
return null;
}
private static List<DexType> getNestedClasses(
DexClass clazz, List<String> nestedClasses, DexDefinitionSupplier definitionSupplier) {
ImmutableList.Builder<DexType> nestedTypes = ImmutableList.builder();
for (String nestedClass : nestedClasses) {
String binaryName =
clazz.type.toBinaryName() + DescriptorUtils.INNER_CLASS_SEPARATOR + nestedClass;
nestedTypes.add(referenceTypeFromBinaryName(binaryName, definitionSupplier));
}
return nestedTypes.build();
}
private static List<DexType> getSealedSubClasses(
DexClass clazz, List<String> sealedSubclasses, DexDefinitionSupplier definitionSupplier) {
ImmutableList.Builder<DexType> sealedTypes = ImmutableList.builder();
for (String sealedSubClass : sealedSubclasses) {
String binaryName =
sealedSubClass.replace(
DescriptorUtils.JAVA_PACKAGE_SEPARATOR, DescriptorUtils.INNER_CLASS_SEPARATOR);
sealedTypes.add(referenceTypeFromBinaryName(binaryName, definitionSupplier));
}
return sealedTypes.build();
}
private static List<KotlinTypeInfo> getSuperTypes(
List<KmType> superTypes, DexDefinitionSupplier definitionSupplier, Reporter reporter) {
ImmutableList.Builder<KotlinTypeInfo> superTypeInfos = ImmutableList.builder();
for (KmType superType : superTypes) {
superTypeInfos.add(KotlinTypeInfo.create(superType, definitionSupplier, reporter));
}
return superTypeInfos.build();
}
private static void setCompanionObject(KmClass kmClass, DexClass hostClass, Reporter reporter) {
String companionObjectName = kmClass.getCompanionObject();
if (companionObjectName == null) {
return;
}
for (DexEncodedField field : hostClass.fields()) {
if (field.field.name.toString().equals(companionObjectName)) {
field.setKotlinMemberInfo(new KotlinCompanionInfo());
return;
}
}
reporter.warning(
KotlinMetadataDiagnostic.missingCompanionObject(hostClass, companionObjectName));
}
@Override
public boolean isClass() {
return true;
}
@Override
public KotlinClassInfo asClass() {
return this;
}
@Override
public KotlinClassHeader rewrite(
DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
KmClass kmClass = new KmClass();
// TODO(b/154348683): Set flags.
kmClass.setFlags(flags);
// Set potentially renamed class name.
DexString originalDescriptor = clazz.type.descriptor;
DexString rewrittenDescriptor = namingLens.lookupDescriptor(clazz.type);
// If the original descriptor equals the rewritten descriptor, we pick the original name
// to preserve potential errors in the original name. As an example, the kotlin stdlib has
// name: .kotlin/collections/CollectionsKt___CollectionsKt$groupingBy$1, which seems incorrect.
kmClass.setName(
originalDescriptor.equals(rewrittenDescriptor)
? this.name
: KotlinMetadataUtils.kotlinNameFromDescriptor(rewrittenDescriptor));
// Find a companion object.
for (DexEncodedField field : clazz.fields()) {
if (field.getKotlinMemberInfo().isCompanion()) {
field.getKotlinMemberInfo().asCompanion().rewrite(kmClass, field.field, namingLens);
}
}
// Take all not backed constructors because we will never find them in definitions.
for (KotlinConstructorInfo constructorInfo : constructorsWithNoBacking) {
constructorInfo.rewrite(kmClass, null, appView, namingLens);
}
// Find all constructors.
for (DexEncodedMethod method : clazz.methods()) {
if (method.getKotlinMemberInfo().isConstructor()) {
KotlinConstructorInfo constructorInfo = method.getKotlinMemberInfo().asConstructor();
constructorInfo.rewrite(kmClass, method, appView, namingLens);
}
}
// Rewrite functions, type-aliases and type-parameters.
declarationContainerInfo.rewrite(
kmClass::visitFunction,
kmClass::visitProperty,
kmClass::visitTypeAlias,
clazz,
appView,
namingLens);
// Rewrite type parameters.
for (KotlinTypeParameterInfo typeParameter : typeParameters) {
typeParameter.rewrite(kmClass::visitTypeParameter, appView, namingLens);
}
// Rewrite super types.
for (KotlinTypeInfo superType : superTypes) {
superType.rewrite(kmClass::visitSupertype, appView, namingLens);
}
// Rewrite nested classes.
for (DexType nestedClass : nestedClasses) {
if (appView.appInfo().isNonProgramTypeOrLiveProgramType(nestedClass)) {
String descriptor =
KotlinMetadataUtils.kotlinNameFromDescriptor(namingLens.lookupDescriptor(nestedClass));
// If the class is a nested class, it should be on the form Foo.Bar$Baz, where Baz is the
// name we should record.
int innerClassIndex = descriptor.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
kmClass.visitNestedClass(descriptor.substring(innerClassIndex + 1));
}
}
// Rewrite sealed sub classes.
for (DexType sealedSubClass : sealedSubClasses) {
if (appView.appInfo().isNonProgramTypeOrLiveProgramType(sealedSubClass)) {
String descriptor =
KotlinMetadataUtils.kotlinNameFromDescriptor(
namingLens.lookupDescriptor(sealedSubClass));
kmClass.visitSealedSubclass(
descriptor.replace(
DescriptorUtils.INNER_CLASS_SEPARATOR, DescriptorUtils.JAVA_PACKAGE_SEPARATOR));
}
}
// TODO(b/154347404): Understand enum entries.
kmClass.getEnumEntries().addAll(enumEntries);
JvmExtensionsKt.setModuleName(kmClass, moduleName);
if (anonymousObjectOrigin != null) {
JvmExtensionsKt.setAnonymousObjectOriginName(
kmClass, KotlinMetadataUtils.kotlinNameFromDescriptor(anonymousObjectOrigin.descriptor));
}
KotlinClassMetadata.Class.Writer writer = new KotlinClassMetadata.Class.Writer();
kmClass.accept(writer);
return writer.write().getHeader();
}
@Override
public String getPackageName() {
return packageName;
}
}