blob: ed7b49fca807a10114f27ee91e0d11c09414fdfe [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.KotlinMemberInfo.MemberKind.CONSTRUCTOR;
import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_FUNCTION;
import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_PROPERTY_GETTER;
import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_PROPERTY_SETTER;
import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.FUNCTION;
import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.PROPERTY_GETTER;
import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.PROPERTY_SETTER;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.getJvmMethodSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmDeclarationContainer;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmValueParameter;
import kotlinx.metadata.jvm.JvmMethodSignature;
// Provides access to field/method-level Kotlin information.
public abstract class KotlinMemberInfo {
private static final List<KotlinValueParameterInfo> EMPTY_PARAM_INFO = ImmutableList.of();
private static final KotlinMemberInfo NO_KOTLIN_MEMBER_INFO = new NoKotlinMemberInfo();
public static KotlinMemberInfo getNoKotlinMemberInfo() {
return NO_KOTLIN_MEMBER_INFO;
}
public final MemberKind memberKind;
// Original member flags. May be necessary to keep Kotlin-specific flag, e.g., suspend function.
final int flags;
private KotlinMemberInfo(MemberKind memberKind, int flags) {
this.memberKind = memberKind;
this.flags = flags;
}
public boolean isFunctionInfo() {
return false;
}
public KotlinFunctionInfo asFunctionInfo() {
return null;
}
public boolean isFieldInfo() {
return false;
}
public KotlinFieldInfo asFieldInfo() {
return null;
}
public boolean isPropertyInfo() {
return false;
}
public KotlinPropertyInfo asPropertyInfo() {
return null;
}
private static class NoKotlinMemberInfo extends KotlinMemberInfo {
private NoKotlinMemberInfo() {
super(MemberKind.NONE, 0);
}
}
public static class KotlinFunctionInfo extends KotlinMemberInfo {
// Information from original KmValueParameter(s) if available.
final List<KotlinValueParameterInfo> valueParameterInfos;
// Information from original KmFunction.returnType. Null if this is from a KmConstructor.
public final KotlinTypeInfo returnType;
private KotlinFunctionInfo(
MemberKind memberKind,
int flags,
KotlinTypeInfo returnType,
List<KotlinValueParameterInfo> valueParameterInfos) {
super(memberKind, flags);
assert memberKind.isFunction() || memberKind.isConstructor();
this.returnType = returnType;
this.valueParameterInfos = valueParameterInfos;
}
KotlinValueParameterInfo getValueParameterInfo(int i) {
if (valueParameterInfos.isEmpty()) {
return null;
}
if (i < 0 || i >= valueParameterInfos.size()) {
return null;
}
return valueParameterInfos.get(i);
}
@Override
public boolean isFunctionInfo() {
return true;
}
@Override
public KotlinFunctionInfo asFunctionInfo() {
return this;
}
}
public static class KotlinFieldInfo extends KotlinMemberInfo {
// Original property name for (extension) property. Otherwise, null.
final String propertyName;
private KotlinFieldInfo(MemberKind memberKind, int flags, String propertyName) {
super(memberKind, flags);
this.propertyName = propertyName;
}
@Override
public boolean isFieldInfo() {
return true;
}
@Override
public KotlinFieldInfo asFieldInfo() {
return this;
}
}
public static class KotlinPropertyInfo extends KotlinMemberInfo {
// Original getter flags. E.g., for property getter.
final int getterFlags;
// Original setter flags. E.g., for property setter.
final int setterFlags;
// Original property name for (extension) property. Otherwise, null.
final String propertyName;
// Original return type information. This should never be NULL (even for setters without field).
final KotlinTypeInfo returnType;
// Information from original KmValueParameter if available.
final KotlinValueParameterInfo valueParameterInfo;
private KotlinPropertyInfo(
MemberKind memberKind,
int flags,
int getterFlags,
int setterFlags,
String propertyName,
KotlinTypeInfo returnType,
KotlinValueParameterInfo valueParameterInfo) {
super(memberKind, flags);
this.getterFlags = getterFlags;
this.setterFlags = setterFlags;
this.propertyName = propertyName;
this.returnType = returnType;
this.valueParameterInfo = valueParameterInfo;
}
@Override
public KotlinPropertyInfo asPropertyInfo() {
return this;
}
@Override
public boolean isPropertyInfo() {
return true;
}
}
private static KotlinFunctionInfo createFunctionInfoFromConstructor(KmConstructor kmConstructor) {
return createFunctionInfo(
CONSTRUCTOR, kmConstructor.getFlags(), null, kmConstructor.getValueParameters());
}
private static KotlinFunctionInfo createFunctionInfo(
MemberKind memberKind, KmFunction kmFunction) {
return createFunctionInfo(
memberKind,
kmFunction.getFlags(),
kmFunction.getReturnType(),
kmFunction.getValueParameters());
}
private static KotlinFunctionInfo createFunctionInfo(
MemberKind memberKind, int flags, KmType returnType, List<KmValueParameter> valueParameters) {
assert memberKind.isFunction() || memberKind.isConstructor();
KotlinTypeInfo returnTypeInfo = KotlinTypeInfo.create(returnType);
assert memberKind.isFunction() || memberKind.isConstructor();
if (valueParameters.isEmpty()) {
return new KotlinFunctionInfo(memberKind, flags, returnTypeInfo, EMPTY_PARAM_INFO);
}
List<KotlinValueParameterInfo> valueParameterInfos = new ArrayList<>(valueParameters.size());
for (KmValueParameter kmValueParameter : valueParameters) {
valueParameterInfos.add(KotlinValueParameterInfo.fromKmValueParameter(kmValueParameter));
}
return new KotlinFunctionInfo(memberKind, flags, returnTypeInfo, valueParameterInfos);
}
private static KotlinFieldInfo createFieldInfo(MemberKind memberKind, KmProperty kmProperty) {
assert memberKind.isBackingField() || memberKind.isBackingFieldForCompanionObject();
return new KotlinFieldInfo(memberKind, kmProperty.getFlags(), kmProperty.getName());
}
private static KotlinPropertyInfo createPropertyInfo(
MemberKind memberKind, KmProperty kmProperty) {
assert memberKind.isProperty();
return new KotlinPropertyInfo(
memberKind,
kmProperty.getFlags(),
kmProperty.getGetterFlags(),
kmProperty.getSetterFlags(),
kmProperty.getName(),
KotlinTypeInfo.create(kmProperty.getReturnType()),
KotlinValueParameterInfo.fromKmValueParameter(kmProperty.getSetterParameter()));
}
public enum MemberKind {
NONE,
CONSTRUCTOR,
FUNCTION,
EXTENSION_FUNCTION,
COMPANION_OBJECT_BACKING_FIELD,
PROPERTY_BACKING_FIELD,
PROPERTY_GETTER,
PROPERTY_SETTER,
PROPERTY_ANNOTATIONS,
// No backing field for extension property.
EXTENSION_PROPERTY_GETTER,
EXTENSION_PROPERTY_SETTER,
EXTENSION_PROPERTY_ANNOTATIONS;
public boolean isConstructor() {
return this == CONSTRUCTOR;
}
public boolean isFunction() {
return this == FUNCTION || isExtensionFunction();
}
public boolean isExtensionFunction() {
return this == EXTENSION_FUNCTION;
}
public boolean isBackingField() {
return this == PROPERTY_BACKING_FIELD;
}
public boolean isBackingFieldForCompanionObject() {
return this == COMPANION_OBJECT_BACKING_FIELD;
}
public boolean isProperty() {
return isBackingField()
|| isBackingFieldForCompanionObject()
|| this == PROPERTY_GETTER
|| this == PROPERTY_SETTER
|| this == PROPERTY_ANNOTATIONS
|| isExtensionProperty();
}
public boolean isExtensionProperty() {
return this == EXTENSION_PROPERTY_GETTER
|| this == EXTENSION_PROPERTY_SETTER
|| this == EXTENSION_PROPERTY_ANNOTATIONS;
}
}
static void markKotlinMemberInfo(DexClass clazz, KotlinInfo kotlinInfo, Reporter reporter) {
if (kotlinInfo == null || !kotlinInfo.hasDeclarations()) {
return;
}
Map<String, KmConstructor> kmConstructorMap = new HashMap<>();
Map<String, KmFunction> kmFunctionMap = new HashMap<>();
Map<String, KmProperty> kmPropertyFieldMap = new HashMap<>();
Map<String, KmProperty> kmPropertyGetterMap = new HashMap<>();
Map<String, KmProperty> kmPropertySetterMap = new HashMap<>();
KmDeclarationContainer kmDeclarationContainer = kotlinInfo.getDeclarations();
String companionObject = null;
if (kotlinInfo.isClass()) {
companionObject = kotlinInfo.asClass().kmClass.getCompanionObject();
kotlinInfo
.asClass()
.kmClass
.getConstructors()
.forEach(
kmConstructor -> {
JvmMethodSignature methodSignature = getJvmMethodSignature(kmConstructor, reporter);
if (methodSignature != null) {
kmConstructorMap.put(methodSignature.asString(), kmConstructor);
}
});
}
kmDeclarationContainer
.getFunctions()
.forEach(
kmFunction -> {
JvmMethodSignature methodSignature = getJvmMethodSignature(kmFunction, reporter);
if (methodSignature != null) {
kmFunctionMap.put(methodSignature.asString(), kmFunction);
}
});
kmDeclarationContainer.getProperties().forEach(kmProperty -> {
KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty, reporter);
if (propertyProcessor.fieldSignature() != null) {
kmPropertyFieldMap.put(propertyProcessor.fieldSignature().asString(), kmProperty);
}
if (propertyProcessor.getterSignature() != null) {
kmPropertyGetterMap.put(propertyProcessor.getterSignature().asString(), kmProperty);
}
if (propertyProcessor.setterSignature() != null) {
kmPropertySetterMap.put(propertyProcessor.setterSignature().asString(), kmProperty);
}
// TODO(b/151194869): property annotations
});
for (DexEncodedField field : clazz.fields()) {
if (companionObject != null && companionObject.equals(field.field.name.toString())) {
assert kotlinInfo.isClass();
kotlinInfo.asClass().foundCompanionObject(field);
continue;
}
String key = toJvmFieldSignature(field.field).asString();
if (kmPropertyFieldMap.containsKey(key)) {
KmProperty kmProperty = kmPropertyFieldMap.get(key);
field.setKotlinMemberInfo(
createFieldInfo(
clazz == kotlinInfo.clazz
? MemberKind.PROPERTY_BACKING_FIELD
: MemberKind.COMPANION_OBJECT_BACKING_FIELD,
kmProperty));
}
}
for (DexEncodedMethod method : clazz.methods()) {
String key = toJvmMethodSignature(method.method).asString();
if (kmConstructorMap.containsKey(key)) {
// Interestingly we cannot assert that the method is a jvm initializer, because the jvm
// signature can be a different.
method.setKotlinMemberInfo(createFunctionInfoFromConstructor(kmConstructorMap.get(key)));
} else if (kmFunctionMap.containsKey(key)) {
KmFunction kmFunction = kmFunctionMap.get(key);
method.setKotlinMemberInfo(
createFunctionInfo(
isExtension(kmFunction) ? EXTENSION_FUNCTION : FUNCTION, kmFunction));
} else if (kmPropertyGetterMap.containsKey(key)) {
KmProperty kmProperty = kmPropertyGetterMap.get(key);
method.setKotlinMemberInfo(
createPropertyInfo(
isExtension(kmProperty) ? EXTENSION_PROPERTY_GETTER : PROPERTY_GETTER, kmProperty));
} else if (kmPropertySetterMap.containsKey(key)) {
KmProperty kmProperty = kmPropertySetterMap.get(key);
method.setKotlinMemberInfo(
createPropertyInfo(
isExtension(kmProperty) ? EXTENSION_PROPERTY_SETTER : PROPERTY_SETTER, kmProperty));
}
}
}
}