blob: 00e689c7d9c19c5765ddc274d5f5c85a3215e410 [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.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
import static com.android.tools.r8.utils.FunctionUtils.forEachApply;
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.DexItemFactory;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.NamingLens;
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.JvmClassExtensionVisitor;
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<KotlinTypeReference> sealedSubClasses;
private final List<KotlinTypeReference> nestedClasses;
private final List<String> enumEntries;
private final KotlinVersionRequirementInfo versionRequirements;
private final KotlinTypeReference anonymousObjectOrigin;
private final String packageName;
private final KotlinLocalDelegatedPropertyInfo localDelegatedProperties;
private final int[] metadataVersion;
private KotlinClassInfo(
int flags,
String name,
String moduleName,
KotlinDeclarationContainerInfo declarationContainerInfo,
List<KotlinTypeParameterInfo> typeParameters,
List<KotlinConstructorInfo> constructorsWithNoBacking,
List<KotlinTypeInfo> superTypes,
List<KotlinTypeReference> sealedSubClasses,
List<KotlinTypeReference> nestedClasses,
List<String> enumEntries,
KotlinVersionRequirementInfo versionRequirements,
KotlinTypeReference anonymousObjectOrigin,
String packageName,
KotlinLocalDelegatedPropertyInfo localDelegatedProperties,
int[] metadataVersion) {
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.versionRequirements = versionRequirements;
this.anonymousObjectOrigin = anonymousObjectOrigin;
this.packageName = packageName;
this.localDelegatedProperties = localDelegatedProperties;
this.metadataVersion = metadataVersion;
}
public static KotlinClassInfo create(
KmClass kmClass,
String packageName,
int[] metadataVersion,
DexClass hostClass,
DexItemFactory factory,
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, factory, 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, factory, reporter, keepByteCode);
setCompanionObject(kmClass, hostClass, reporter);
return new KotlinClassInfo(
kmClass.getFlags(),
kmClass.name,
JvmExtensionsKt.getModuleName(kmClass),
container,
KotlinTypeParameterInfo.create(kmClass.getTypeParameters(), factory, reporter),
notBackedConstructors.build(),
getSuperTypes(kmClass.getSupertypes(), factory, reporter),
getSealedSubClasses(kmClass.getSealedSubclasses(), factory),
getNestedClasses(hostClass, kmClass.getNestedClasses(), factory),
kmClass.getEnumEntries(),
KotlinVersionRequirementInfo.create(kmClass.getVersionRequirements()),
getAnonymousObjectOrigin(kmClass, factory),
packageName,
KotlinLocalDelegatedPropertyInfo.create(
JvmExtensionsKt.getLocalDelegatedProperties(kmClass), factory, reporter),
metadataVersion);
}
private static KotlinTypeReference getAnonymousObjectOrigin(
KmClass kmClass, DexItemFactory factory) {
String anonymousObjectOriginName = JvmExtensionsKt.getAnonymousObjectOriginName(kmClass);
if (anonymousObjectOriginName != null) {
return KotlinTypeReference.fromBinaryName(anonymousObjectOriginName, factory);
}
return null;
}
private static List<KotlinTypeReference> getNestedClasses(
DexClass clazz, List<String> nestedClasses, DexItemFactory factory) {
ImmutableList.Builder<KotlinTypeReference> nestedTypes = ImmutableList.builder();
for (String nestedClass : nestedClasses) {
String binaryName =
clazz.type.toBinaryName() + DescriptorUtils.INNER_CLASS_SEPARATOR + nestedClass;
nestedTypes.add(KotlinTypeReference.fromBinaryName(binaryName, factory));
}
return nestedTypes.build();
}
private static List<KotlinTypeReference> getSealedSubClasses(
List<String> sealedSubclasses, DexItemFactory factory) {
ImmutableList.Builder<KotlinTypeReference> sealedTypes = ImmutableList.builder();
for (String sealedSubClass : sealedSubclasses) {
String binaryName =
sealedSubClass.replace(
DescriptorUtils.JAVA_PACKAGE_SEPARATOR, DescriptorUtils.INNER_CLASS_SEPARATOR);
sealedTypes.add(KotlinTypeReference.fromBinaryName(binaryName, factory));
}
return sealedTypes.build();
}
private static List<KotlinTypeInfo> getSuperTypes(
List<KmType> superTypes, DexItemFactory factory, Reporter reporter) {
ImmutableList.Builder<KotlinTypeInfo> superTypeInfos = ImmutableList.builder();
for (KmType superType : superTypes) {
superTypeInfos.add(KotlinTypeInfo.create(superType, factory, 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<?> 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
: DescriptorUtils.getBinaryNameFromDescriptor(rewrittenDescriptor.toString()));
// 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 (KotlinTypeReference nestedClass : nestedClasses) {
String nestedDescriptor = nestedClass.toRenamedBinaryNameOrDefault(appView, namingLens, null);
if (nestedDescriptor != null) {
// 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 = nestedDescriptor.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
kmClass.visitNestedClass(nestedDescriptor.substring(innerClassIndex + 1));
}
}
// Rewrite sealed sub classes.
for (KotlinTypeReference sealedSubClass : sealedSubClasses) {
String sealedDescriptor =
sealedSubClass.toRenamedBinaryNameOrDefault(appView, namingLens, null);
if (sealedDescriptor != null) {
kmClass.visitSealedSubclass(
sealedDescriptor.replace(
DescriptorUtils.INNER_CLASS_SEPARATOR, DescriptorUtils.JAVA_PACKAGE_SEPARATOR));
}
}
// TODO(b/154347404): Understand enum entries.
kmClass.getEnumEntries().addAll(enumEntries);
versionRequirements.rewrite(kmClass::visitVersionRequirement);
JvmClassExtensionVisitor extensionVisitor =
(JvmClassExtensionVisitor) kmClass.visitExtensions(JvmClassExtensionVisitor.TYPE);
extensionVisitor.visitModuleName(moduleName);
if (anonymousObjectOrigin != null) {
String renamedAnon =
anonymousObjectOrigin.toRenamedBinaryNameOrDefault(appView, namingLens, null);
if (renamedAnon != null) {
extensionVisitor.visitAnonymousObjectOriginName(renamedAnon);
}
}
localDelegatedProperties.rewrite(
extensionVisitor::visitLocalDelegatedProperty, appView, namingLens);
extensionVisitor.visitEnd();
KotlinClassMetadata.Class.Writer writer = new KotlinClassMetadata.Class.Writer();
kmClass.accept(writer);
return writer.write().getHeader();
}
@Override
public String getPackageName() {
return packageName;
}
@Override
public int[] getMetadataVersion() {
return metadataVersion;
}
@Override
public void trace(DexDefinitionSupplier definitionSupplier) {
forEachApply(constructorsWithNoBacking, constructor -> constructor::trace, definitionSupplier);
declarationContainerInfo.trace(definitionSupplier);
forEachApply(typeParameters, param -> param::trace, definitionSupplier);
forEachApply(superTypes, type -> type::trace, definitionSupplier);
forEachApply(sealedSubClasses, sealed -> sealed::trace, definitionSupplier);
forEachApply(nestedClasses, nested -> nested::trace, definitionSupplier);
localDelegatedProperties.trace(definitionSupplier);
// TODO(b/154347404): trace enum entries.
if (anonymousObjectOrigin != null) {
anonymousObjectOrigin.trace(definitionSupplier);
}
}
}