blob: 855a71f7e0f673332282ec57ef59a4c41c353f78 [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.Kotlin.addKotlinPrefix;
import com.android.tools.r8.errors.InvalidDescriptorException;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmConstructorExtensionVisitor;
import kotlinx.metadata.KmConstructorVisitor;
import kotlinx.metadata.KmExtensionType;
import kotlinx.metadata.KmFunction;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmPropertyExtensionVisitor;
import kotlinx.metadata.KmPropertyVisitor;
import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor;
import kotlinx.metadata.jvm.JvmExtensionsKt;
import kotlinx.metadata.jvm.JvmFieldSignature;
import kotlinx.metadata.jvm.JvmMethodSignature;
import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
class KotlinMetadataJvmExtensionUtils {
// Mappings from Kotlin types to JVM types (of String)
private static final Map<String, String> knownTypeConversion =
// See {@link org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite}
ImmutableMap.<String, String>builder()
// Boxed primitives and arrays
.put(addKotlinPrefix("Boolean;"), "Z")
.put(addKotlinPrefix("BooleanArray;"), "[Z")
.put(addKotlinPrefix("Byte;"), "B")
.put(addKotlinPrefix("ByteArray;"), "[B")
.put(addKotlinPrefix("Char;"), "C")
.put(addKotlinPrefix("CharArray;"), "[C")
.put(addKotlinPrefix("Short;"), "S")
.put(addKotlinPrefix("ShortArray;"), "[S")
.put(addKotlinPrefix("Int;"), "I")
.put(addKotlinPrefix("IntArray;"), "[I")
.put(addKotlinPrefix("Long;"), "J")
.put(addKotlinPrefix("LongArray;"), "[J")
.put(addKotlinPrefix("Float;"), "F")
.put(addKotlinPrefix("FloatArray;"), "[F")
.put(addKotlinPrefix("Double;"), "D")
.put(addKotlinPrefix("DoubleArray;"), "[D")
// Other intrinsics
.put(addKotlinPrefix("Unit;"), "V")
.put(addKotlinPrefix("Any;"), "Ljava/lang/Object;")
.put(addKotlinPrefix("Nothing;"), "Ljava/lang/Void;")
.putAll(ImmutableList.of(
"String", "CharSequence", "Throwable", "Cloneable", "Number", "Comparable", "Enum")
.stream().collect(Collectors.toMap(
t -> addKotlinPrefix(t + ";"),
t -> "Ljava/lang/" + t + ";")))
// Collections
.putAll(ImmutableList.of("Iterator", "Collection", "List", "Set", "Map", "ListIterator")
.stream().collect(Collectors.toMap(
t -> addKotlinPrefix("collections/" + t + ";"),
t -> "Ljava/util/" + t + ";")))
.putAll(ImmutableList.of("Iterator", "Collection", "List", "Set", "Map", "ListIterator")
.stream().collect(Collectors.toMap(
t -> addKotlinPrefix("collections/Mutable" + t + ";"),
t -> "Ljava/util/" + t + ";")))
.put(addKotlinPrefix("collections/Iterable;"), "Ljava/lang/Iterable;")
.put(addKotlinPrefix("collections/MutableIterable;"), "Ljava/lang/Iterable;")
.put(addKotlinPrefix("collections/Map.Entry;"), "Ljava/util/Map$Entry;")
.put(addKotlinPrefix("collections/MutableMap.MutableEntry;"), "Ljava/util/Map$Entry;")
// .../FunctionN -> .../jvm/functions/FunctionN
.putAll(
IntStream.rangeClosed(0, 22).boxed().collect(Collectors.toMap(
i -> addKotlinPrefix("Function" + i + ";"),
i -> addKotlinPrefix("jvm/functions/Function" + i + ";"))))
.build();
// TODO(b/151195430): remove backward type conversions.
private static String remapKotlinType(String type) {
if (knownTypeConversion.containsKey(type)) {
return knownTypeConversion.get(type);
}
return type;
}
// TODO(b/151195430): remove backward type conversions.
// Kotlin @Metadata deserialization has plain "kotlin", which will be relocated in r8lib.
// See b/70169921#comment57 for more details.
// E.g., desc: (Labc/xyz/C;Lkotlin/Function1;)kotlin/Unit
// remapped desc would be: (Labc/xyz/C;Lkotlin/jvm/functions/Function1;)V
private static String remapKotlinTypeInDesc(String desc, Reporter reporter) {
if (desc == null) {
return null;
}
if (desc.isEmpty()) {
return desc;
}
String[] parameterTypes;
try {
parameterTypes = DescriptorUtils.getArgumentTypeDescriptors(desc);
for (int i = 0; i < parameterTypes.length; i++) {
parameterTypes[i] = remapKotlinType(parameterTypes[i]);
}
} catch (InvalidDescriptorException e) {
// JvmMethodSignature from @Metadata is not 100% reliable (due to its own optimization using
// map, relocation in r8lib, etc.)
reporter.info(
new StringDiagnostic(
"Invalid descriptor (deserialized from Kotlin @Metadata): " + desc));
return desc;
}
int index = desc.indexOf(')');
assert 0 < index && index < desc.length() : desc;
String returnType = remapKotlinType(desc.substring(index + 1));
return "(" + StringUtils.join(Arrays.asList(parameterTypes), "") + ")" + returnType;
}
static JvmFieldSignature toJvmFieldSignature(DexField field) {
return new JvmFieldSignature(field.name.toString(), field.type.toDescriptorString());
}
static JvmMethodSignature toJvmMethodSignature(DexMethod method) {
StringBuilder descBuilder = new StringBuilder();
descBuilder.append("(");
for (DexType argType : method.proto.parameters.values) {
descBuilder.append(argType.toDescriptorString());
}
descBuilder.append(")");
descBuilder.append(method.proto.returnType.toDescriptorString());
return new JvmMethodSignature(method.name.toString(), descBuilder.toString());
}
static class KmConstructorProcessor {
private JvmMethodSignature signature = null;
KmConstructorProcessor(KmConstructor kmConstructor, Reporter reporter) {
kmConstructor.accept(new KmConstructorVisitor() {
@Override
public KmConstructorExtensionVisitor visitExtensions(KmExtensionType type) {
if (type != JvmConstructorExtensionVisitor.TYPE) {
return null;
}
return new JvmConstructorExtensionVisitor() {
@Override
public void visit(JvmMethodSignature desc) {
assert signature == null : signature.asString();
signature = desc;
}
};
}
});
if (signature != null) {
String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter);
if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) {
signature = new JvmMethodSignature(signature.getName(), remappedDesc);
}
}
}
JvmMethodSignature signature() {
return signature;
}
}
// Custom name via @JvmName("..."). Otherwise, null.
static JvmMethodSignature getJvmMethodSignature(KmConstructor kmConstructor, Reporter reporter) {
return remapJvmMethodSignature(JvmExtensionsKt.getSignature(kmConstructor), reporter);
}
// Custom name via @JvmName("..."). Otherwise, null.
static JvmMethodSignature getJvmMethodSignature(KmFunction kmFunction, Reporter reporter) {
return remapJvmMethodSignature(JvmExtensionsKt.getSignature(kmFunction), reporter);
}
private static JvmMethodSignature remapJvmMethodSignature(
JvmMethodSignature signature, Reporter reporter) {
if (signature != null) {
String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter);
if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) {
signature = new JvmMethodSignature(signature.getName(), remappedDesc);
}
}
return signature;
}
static class KmPropertyProcessor {
private JvmFieldSignature fieldSignature = null;
// Custom getter via @get:JvmName("..."). Otherwise, null.
private JvmMethodSignature getterSignature = null;
// Custom getter via @set:JvmName("..."). Otherwise, null.
private JvmMethodSignature setterSignature = null;
KmPropertyProcessor(KmProperty kmProperty, Reporter reporter) {
kmProperty.accept(new KmPropertyVisitor() {
@Override
public KmPropertyExtensionVisitor visitExtensions(KmExtensionType type) {
if (type != JvmPropertyExtensionVisitor.TYPE) {
return null;
}
return new JvmPropertyExtensionVisitor() {
@Override
public void visit(
int flags,
JvmFieldSignature fieldDesc,
JvmMethodSignature getterDesc,
JvmMethodSignature setterDesc) {
assert fieldSignature == null : fieldSignature.asString();
fieldSignature = fieldDesc;
assert getterSignature == null : getterSignature.asString();
getterSignature = getterDesc;
assert setterSignature == null : setterSignature.asString();
setterSignature = setterDesc;
}
};
}
});
if (fieldSignature != null) {
String remappedDesc = remapKotlinType(fieldSignature.getDesc());
if (remappedDesc != null && !remappedDesc.equals(fieldSignature.getDesc())) {
fieldSignature = new JvmFieldSignature(fieldSignature.getName(), remappedDesc);
}
}
if (getterSignature != null) {
String remappedDesc = remapKotlinTypeInDesc(getterSignature.getDesc(), reporter);
if (remappedDesc != null && !remappedDesc.equals(getterSignature.getDesc())) {
getterSignature = new JvmMethodSignature(getterSignature.getName(), remappedDesc);
}
}
if (setterSignature != null) {
String remappedDesc = remapKotlinTypeInDesc(setterSignature.getDesc(), reporter);
if (remappedDesc != null && !remappedDesc.equals(setterSignature.getDesc())) {
setterSignature = new JvmMethodSignature(setterSignature.getName(), remappedDesc);
}
}
}
JvmFieldSignature fieldSignature() {
return fieldSignature;
}
JvmMethodSignature getterSignature() {
return getterSignature;
}
JvmMethodSignature setterSignature() {
return setterSignature;
}
}
}