Add more conversions from Kotlin types to JVM types, and vice versa.
Bug: 143687784, 70169921
Change-Id: I163e51fdd89896b8c961db077df053f09f8cbac9
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ed70c22..78471d6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -887,7 +887,7 @@
for (DexProgramClass programClass : application.classes()) {
KotlinInfo kotlinInfo = kotlin.getKotlinInfo(programClass, reporter);
programClass.setKotlinInfo(kotlinInfo);
- KotlinMemberInfo.markKotlinMemberInfo(programClass, kotlinInfo);
+ KotlinMemberInfo.markKotlinMemberInfo(programClass, kotlinInfo, reporter);
}
}
diff --git a/src/main/java/com/android/tools/r8/errors/InvalidDescriptorException.java b/src/main/java/com/android/tools/r8/errors/InvalidDescriptorException.java
new file mode 100644
index 0000000..f6702b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InvalidDescriptorException.java
@@ -0,0 +1,10 @@
+// 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.errors;
+
+public class InvalidDescriptorException extends InternalCompilerError {
+ public InvalidDescriptorException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 7d568f6..04d2cc3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -122,6 +122,15 @@
public final DexString voidDescriptor = createString("V");
public final DexString descriptorSeparator = createString("/");
+ private final DexString booleanArrayDescriptor = createString("[Z");
+ private final DexString byteArrayDescriptor = createString("[B");
+ private final DexString charArrayDescriptor = createString("[C");
+ private final DexString doubleArrayDescriptor = createString("[D");
+ private final DexString floatArrayDescriptor = createString("[F");
+ private final DexString intArrayDescriptor = createString("[I");
+ private final DexString longArrayDescriptor = createString("[J");
+ private final DexString shortArrayDescriptor = createString("[S");
+
public final DexString boxedBooleanDescriptor = createString("Ljava/lang/Boolean;");
public final DexString boxedByteDescriptor = createString("Ljava/lang/Byte;");
public final DexString boxedCharDescriptor = createString("Ljava/lang/Character;");
@@ -131,6 +140,7 @@
public final DexString boxedLongDescriptor = createString("Ljava/lang/Long;");
public final DexString boxedShortDescriptor = createString("Ljava/lang/Short;");
public final DexString boxedNumberDescriptor = createString("Ljava/lang/Number;");
+ public final DexString boxedVoidDescriptor = createString("Ljava/lang/Void;");
public final DexString unboxBooleanMethodName = createString("booleanValue");
public final DexString unboxByteMethodName = createString("byteValue");
@@ -261,15 +271,14 @@
public final DexString newUpdaterName = createString("newUpdater");
public final DexString constructorMethodName = createString(Constants.INSTANCE_INITIALIZER_NAME);
- public final DexString classConstructorMethodName = createString(Constants.CLASS_INITIALIZER_NAME);
+ public final DexString classConstructorMethodName =
+ createString(Constants.CLASS_INITIALIZER_NAME);
public final DexString thisName = createString("this");
public final DexString enumValuesFieldName = createString("$VALUES");
public final DexString enabledFieldName = createString("ENABLED");
- private final DexString charArrayDescriptor = createString("[C");
- private final DexType charArrayType = createType(charArrayDescriptor);
public final DexString throwableArrayDescriptor = createString("[Ljava/lang/Throwable;");
public final DexType booleanType = createType(booleanDescriptor);
@@ -282,6 +291,15 @@
public final DexType shortType = createType(shortDescriptor);
public final DexType voidType = createType(voidDescriptor);
+ public final DexType booleanArrayType = createType(booleanArrayDescriptor);
+ public final DexType byteArrayType = createType(byteArrayDescriptor);
+ public final DexType charArrayType = createType(charArrayDescriptor);
+ public final DexType doubleArrayType = createType(doubleArrayDescriptor);
+ public final DexType floatArrayType = createType(floatArrayDescriptor);
+ public final DexType intArrayType = createType(intArrayDescriptor);
+ public final DexType longArrayType = createType(longArrayDescriptor);
+ public final DexType shortArrayType = createType(shortArrayDescriptor);
+
public final DexType boxedBooleanType = createType(boxedBooleanDescriptor);
public final DexType boxedByteType = createType(boxedByteDescriptor);
public final DexType boxedCharType = createType(boxedCharDescriptor);
@@ -291,6 +309,7 @@
public final DexType boxedLongType = createType(boxedLongDescriptor);
public final DexType boxedShortType = createType(boxedShortDescriptor);
public final DexType boxedNumberType = createType(boxedNumberDescriptor);
+ public final DexType boxedVoidType = createType(boxedVoidDescriptor);
public final DexType charSequenceType = createType(charSequenceDescriptor);
public final DexType charSequenceArrayType = createType(charSequenceArrayDescriptor);
@@ -425,6 +444,7 @@
public final DexType enumerationType = createType("Ljava/util/Enumeration;");
public final DexType serializableType = createType("Ljava/io/Serializable;");
public final DexType externalizableType = createType("Ljava/io/Externalizable;");
+ public final DexType cloneableType = createType("Ljava/lang/Cloneable;");
public final DexType comparableType = createType("Ljava/lang/Comparable;");
public final ServiceLoaderMethods serviceLoaderMethods = new ServiceLoaderMethods();
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 34a98eb..941d7e2 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -40,6 +40,7 @@
public final Intrinsics intrinsics;
public final Metadata metadata;
+ // Mappings from JVM types to Kotlin types (of type DexType)
final Map<DexType, DexType> knownTypeConversion;
public Kotlin(DexItemFactory factory) {
@@ -50,20 +51,44 @@
this.intrinsics = new Intrinsics();
this.metadata = new Metadata();
+ // See {@link org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite}
this.knownTypeConversion =
ImmutableMap.<DexType, DexType>builder()
// https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html
+ // Boxed primitives and arrays
.put(factory.booleanType, factory.createType(addKotlinPrefix("Boolean;")))
+ .put(factory.booleanArrayType, factory.createType(addKotlinPrefix("BooleanArray;")))
.put(factory.byteType, factory.createType(addKotlinPrefix("Byte;")))
- .put(factory.charType, factory.createType(addKotlinPrefix("Character;")))
+ .put(factory.byteArrayType, factory.createType(addKotlinPrefix("ByteArray;")))
+ .put(factory.charType, factory.createType(addKotlinPrefix("Char;")))
+ .put(factory.charArrayType, factory.createType(addKotlinPrefix("CharArray;")))
.put(factory.shortType, factory.createType(addKotlinPrefix("Short;")))
+ .put(factory.shortArrayType, factory.createType(addKotlinPrefix("ShortArray;")))
.put(factory.intType, factory.createType(addKotlinPrefix("Int;")))
+ .put(factory.intArrayType, factory.createType(addKotlinPrefix("IntArray;")))
.put(factory.longType, factory.createType(addKotlinPrefix("Long;")))
+ .put(factory.longArrayType, factory.createType(addKotlinPrefix("LongArray;")))
.put(factory.floatType, factory.createType(addKotlinPrefix("Float;")))
+ .put(factory.floatArrayType, factory.createType(addKotlinPrefix("FloatArray;")))
.put(factory.doubleType, factory.createType(addKotlinPrefix("Double;")))
+ .put(factory.doubleArrayType, factory.createType(addKotlinPrefix("DoubleArray;")))
+ // Other intrinsics
.put(factory.voidType, factory.createType(addKotlinPrefix("Unit;")))
+ .put(factory.objectType, factory.createType(addKotlinPrefix("Any;")))
+ .put(factory.boxedVoidType, factory.createType(addKotlinPrefix("Nothing;")))
.put(factory.stringType, factory.createType(addKotlinPrefix("String;")))
- // TODO(b/70169921): Collections?
+ .put(factory.charSequenceType, factory.createType(addKotlinPrefix("CharSequence;")))
+ .put(factory.throwableType, factory.createType(addKotlinPrefix("Throwable;")))
+ .put(factory.cloneableType, factory.createType(addKotlinPrefix("Cloneable;")))
+ .put(factory.boxedNumberType, factory.createType(addKotlinPrefix("Number;")))
+ .put(factory.comparableType, factory.createType(addKotlinPrefix("Comparable;")))
+ .put(factory.enumType, factory.createType(addKotlinPrefix("Enum;")))
+ // TODO(b/70169921): (Mutable) Collections?
+ // .../jvm/functions/FunctionN -> .../FunctionN
+ .putAll(
+ IntStream.rangeClosed(0, 22).boxed().collect(Collectors.toMap(
+ i -> factory.createType(addKotlinPrefix("jvm/functions/Function" + i + ";")),
+ i -> factory.createType(addKotlinPrefix("Function" + i + ";")))))
.build();
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
index 5382e44..62b5355 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmFunctionProcessor;
import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor;
+import com.android.tools.r8.utils.Reporter;
import java.util.HashMap;
import java.util.Map;
import kotlinx.metadata.KmDeclarationContainer;
@@ -88,36 +89,37 @@
}
}
- public static void markKotlinMemberInfo(DexClass clazz, KotlinInfo kotlinInfo) {
+ public static void markKotlinMemberInfo(
+ DexClass clazz, KotlinInfo kotlinInfo, Reporter reporter) {
if (kotlinInfo == null || !kotlinInfo.hasDeclarations()) {
return;
}
if (kotlinInfo.isClass()) {
- markKotlinMemberInfo(clazz, kotlinInfo.asClass().kmClass);
+ markKotlinMemberInfo(clazz, kotlinInfo.asClass().kmClass, reporter);
} else if (kotlinInfo.isFile()) {
- markKotlinMemberInfo(clazz, kotlinInfo.asFile().kmPackage);
+ markKotlinMemberInfo(clazz, kotlinInfo.asFile().kmPackage, reporter);
} else if (kotlinInfo.isClassPart()) {
- markKotlinMemberInfo(clazz, kotlinInfo.asClassPart().kmPackage);
+ markKotlinMemberInfo(clazz, kotlinInfo.asClassPart().kmPackage, reporter);
} else {
throw new Unreachable("Unexpected KotlinInfo: " + kotlinInfo);
}
}
private static void markKotlinMemberInfo(
- DexClass clazz, KmDeclarationContainer kmDeclarationContainer) {
+ DexClass clazz, KmDeclarationContainer kmDeclarationContainer, Reporter reporter) {
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.getFunctions().forEach(kmFunction -> {
- KmFunctionProcessor functionProcessor = new KmFunctionProcessor(kmFunction);
+ KmFunctionProcessor functionProcessor = new KmFunctionProcessor(kmFunction, reporter);
if (functionProcessor.signature() != null) {
kmFunctionMap.put(functionProcessor.signature().asString(), kmFunction);
}
});
kmDeclarationContainer.getProperties().forEach(kmProperty -> {
- KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty);
+ KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty, reporter);
if (propertyProcessor.fieldSignature() != null) {
kmPropertyFieldMap.put(propertyProcessor.fieldSignature().asString(), kmProperty);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
index bdd7f60..fadf9cb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
@@ -3,9 +3,22 @@
// 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;
@@ -24,6 +37,94 @@
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();
+
+ private static String remapKotlinType(String type) {
+ if (knownTypeConversion.containsKey(type)) {
+ return knownTypeConversion.get(type);
+ }
+ return type;
+ }
+
+ // 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());
}
@@ -42,7 +143,7 @@
static class KmConstructorProcessor {
private JvmMethodSignature signature = null;
- KmConstructorProcessor(KmConstructor kmConstructor) {
+ KmConstructorProcessor(KmConstructor kmConstructor, Reporter reporter) {
kmConstructor.accept(new KmConstructorVisitor() {
@Override
public KmConstructorExtensionVisitor visitExtensions(KmExtensionType type) {
@@ -58,6 +159,12 @@
};
}
});
+ if (signature != null) {
+ String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter);
+ if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) {
+ signature = new JvmMethodSignature(signature.getName(), remappedDesc);
+ }
+ }
}
JvmMethodSignature signature() {
@@ -69,7 +176,7 @@
// Custom name via @JvmName("..."). Otherwise, null.
private JvmMethodSignature signature = null;
- KmFunctionProcessor(KmFunction kmFunction) {
+ KmFunctionProcessor(KmFunction kmFunction, Reporter reporter) {
kmFunction.accept(new KmFunctionVisitor() {
@Override
public KmFunctionExtensionVisitor visitExtensions(KmExtensionType type) {
@@ -85,6 +192,12 @@
};
}
});
+ if (signature != null) {
+ String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter);
+ if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) {
+ signature = new JvmMethodSignature(signature.getName(), remappedDesc);
+ }
+ }
}
JvmMethodSignature signature() {
@@ -99,7 +212,7 @@
// Custom getter via @set:JvmName("..."). Otherwise, null.
private JvmMethodSignature setterSignature = null;
- KmPropertyProcessor(KmProperty kmProperty) {
+ KmPropertyProcessor(KmProperty kmProperty, Reporter reporter) {
kmProperty.accept(new KmPropertyVisitor() {
@Override
public KmPropertyExtensionVisitor visitExtensions(KmExtensionType type) {
@@ -123,6 +236,24 @@
};
}
});
+ 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() {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
index c46b94a..94c65e3 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -46,16 +46,16 @@
static String toRenamedClassifier(
DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- // E.g., [Ljava/lang/String; -> kotlin/Array
- if (type.isArrayType()) {
- return NAME + "/Array";
- }
- // E.g., void -> kotlin/Unit
+ // E.g., V -> kotlin/Unit, J -> kotlin/Long, [J -> kotlin/LongArray
if (appView.dexItemFactory().kotlin.knownTypeConversion.containsKey(type)) {
DexType convertedType = appView.dexItemFactory().kotlin.knownTypeConversion.get(type);
assert convertedType != null;
return descriptorToKotlinClassifier(convertedType.toDescriptorString());
}
+ // E.g., [Ljava/lang/String; -> kotlin/Array
+ if (type.isArrayType()) {
+ return NAME + "/Array";
+ }
// For library or classpath class, synthesize @Metadata always.
// For a program class, make sure it is live.
if (!appView.appInfo().isNonProgramTypeOrLiveProgramType(type)) {
@@ -79,6 +79,9 @@
// and/or why wiping out flags works for KmType but not KmFunction?!
KmType kmType = new KmType(flagsOf());
kmType.visitClass(classifier);
+ // TODO(b/70169921): Need to set arguments as type parameter.
+ // E.g., for kotlin/Function1<P1, R>, P1 and R are recorded inside KmType.arguments, which
+ // enables kotlinc to resolve `this` type and return type of the lambda.
return kmType;
}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 70d6df0..b532e50 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.utils.FileUtils.MODULES_PREFIX;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InvalidDescriptorException;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -370,6 +371,12 @@
return 'L' + className.replace(JAVA_PACKAGE_SEPARATOR, INNER_CLASS_SEPARATOR) + ';';
}
+ // Kotlin @Metadata deserialization has plain "kotlin", which will be relocated in r8lib.
+ // See b/70169921#comment25 for more details.
+ private static String backwardRelocatedName(String name) {
+ return name.replace("com/android/tools/r8/jetbrains/", "");
+ }
+
/**
* Get a fully qualified name from a classifier in Kotlin metadata.
* @param kmType where classifier contains Kotlin internal name, like "org/foo/bar/Baz.Nested"
@@ -385,16 +392,16 @@
public void visitClass(String name) {
// TODO(b/70169921): Remove this if metadata lib is resilient to namespace relocation.
// See b/70169921#comment25 for more details.
- String backwardRelocatedName = name.replace("com/android/tools/r8/jetbrains/", "");
- descriptor.set(getDescriptorFromKotlinClassifier(backwardRelocatedName));
+ assert descriptor.get() == null : descriptor.get();
+ descriptor.set(getDescriptorFromKotlinClassifier(backwardRelocatedName(name)));
}
@Override
public void visitTypeAlias(String name) {
// TODO(b/70169921): Remove this if metadata lib is resilient to namespace relocation.
// See b/70169921#comment25 for more details.
- String backwardRelocatedName = name.replace("com/android/tools/r8/jetbrains/", "");
- descriptor.set(getDescriptorFromKotlinClassifier(backwardRelocatedName));
+ assert descriptor.get() == null : descriptor.get();
+ descriptor.set(getDescriptorFromKotlinClassifier(backwardRelocatedName(name)));
}
});
return descriptor.get();
@@ -619,7 +626,7 @@
while ((c = methodDescriptor.charAt(charIdx)) != ')') {
switch (c) {
case 'V':
- throw new Unreachable();
+ throw new InvalidDescriptorException(methodDescriptor);
case 'Z':
case 'C':
case 'B':
@@ -646,7 +653,7 @@
argDescriptors[argIdx++] = methodDescriptor.substring(startType, charIdx + 1);
break;
default:
- throw new Unreachable();
+ throw new InvalidDescriptorException(methodDescriptor);
}
charIdx++;
}
@@ -657,7 +664,7 @@
int charIdx = 1;
char c;
int argCount = 0;
- while ((c = methodDescriptor.charAt(charIdx++)) != ')') {
+ while (charIdx < methodDescriptor.length() && (c = methodDescriptor.charAt(charIdx++)) != ')') {
if (c == 'L') {
while (methodDescriptor.charAt(charIdx++) != ';')
;
@@ -666,6 +673,9 @@
argCount++;
}
}
+ if (charIdx >= methodDescriptor.length() || methodDescriptor.charAt(charIdx - 1) != ')') {
+ throw new InvalidDescriptorException(methodDescriptor);
+ }
return argCount;
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index 917ede7..1f8fee7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -7,8 +7,10 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
@@ -20,6 +22,7 @@
import com.android.tools.r8.utils.codeinspector.KmClassSubject;
import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
import com.android.tools.r8.utils.codeinspector.KmPackageSubject;
+import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
@@ -94,7 +97,6 @@
private void inspectMerged(CodeInspector inspector) {
String superClassName = PKG + ".extension_function_lib.Super";
String bClassName = PKG + ".extension_function_lib.B";
- String bKtClassName = PKG + ".extension_function_lib.BKt";
assertThat(inspector.clazz(superClassName), not(isPresent()));
@@ -108,15 +110,7 @@
assertTrue(superTypes.stream().noneMatch(
supertype -> supertype.getFinalDescriptor().contains("Super")));
- ClassSubject bKt = inspector.clazz(bKtClassName);
- assertThat(bKt, isPresent());
- assertThat(bKt, not(isRenamed()));
- // API entry is kept, hence the presence of Metadata.
- KmPackageSubject kmPackage = bKt.getKmPackage();
- assertThat(kmPackage, isPresent());
-
- KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
- assertThat(kmFunction, isExtensionFunction());
+ inspectExtensions(inspector);
}
@Test
@@ -154,7 +148,6 @@
private void inspectRenamed(CodeInspector inspector) {
String superClassName = PKG + ".extension_function_lib.Super";
String bClassName = PKG + ".extension_function_lib.B";
- String bKtClassName = PKG + ".extension_function_lib.BKt";
ClassSubject sup = inspector.clazz(superClassName);
assertThat(sup, isPresent());
@@ -172,6 +165,16 @@
assertTrue(superTypes.stream().anyMatch(
supertype -> supertype.getFinalDescriptor().equals(sup.getFinalDescriptor())));
+ inspectExtensions(inspector);
+ }
+
+ private void inspectExtensions(CodeInspector inspector) {
+ String bClassName = PKG + ".extension_function_lib.B";
+ String bKtClassName = PKG + ".extension_function_lib.BKt";
+
+ ClassSubject impl = inspector.clazz(bClassName);
+ assertThat(impl, isPresent());
+
ClassSubject bKt = inspector.clazz(bKtClassName);
assertThat(bKt, isPresent());
assertThat(bKt, not(isRenamed()));
@@ -181,5 +184,29 @@
KmFunctionSubject kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("extension");
assertThat(kmFunction, isExtensionFunction());
+ KmTypeSubject kmTypeSubject = kmFunction.receiverParameterType();
+ assertEquals(impl.getFinalDescriptor(), kmTypeSubject.descriptor());
+
+ kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("csHash");
+ assertThat(kmFunction, isExtensionFunction());
+ kmTypeSubject = kmFunction.receiverParameterType();
+ assertEquals("Lkotlin/CharSequence;", kmTypeSubject.descriptor());
+ kmTypeSubject = kmFunction.returnType();
+ assertEquals("Lkotlin/Long;", kmTypeSubject.descriptor());
+
+ kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("longArrayHash");
+ assertThat(kmFunction, isExtensionFunction());
+ kmTypeSubject = kmFunction.receiverParameterType();
+ assertEquals("Lkotlin/LongArray;", kmTypeSubject.descriptor());
+ kmTypeSubject = kmFunction.returnType();
+ assertEquals("Lkotlin/Long;", kmTypeSubject.descriptor());
+
+ kmFunction = kmPackage.kmFunctionExtensionWithUniqueName("myApply");
+ assertThat(kmFunction, isExtensionFunction());
+ kmTypeSubject = kmFunction.receiverParameterType();
+ assertEquals(impl.getFinalDescriptor(), kmTypeSubject.descriptor());
+ // TODO(b/70169921): Check param[0] has type kotlin/Function1<(renamed) B, Unit>
+ String desc = kmFunction.signature().getDesc();
+ assertThat(desc, containsString("kotlin/jvm/functions/Function1"));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index e0c17a8..f4e79f3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -88,12 +88,6 @@
assertThat(
kotlinTestCompileResult.stderr,
containsString("type mismatch: inferred type is String but Array<T> was expected"));
- assertThat(
- kotlinTestCompileResult.stderr,
- containsString("(???, ???) -> [ERROR : <ERROR FUNCTION RETURN TYPE>]"));
- assertThat(
- kotlinTestCompileResult.stderr,
- containsString("but kotlin.jvm.functions.Function2<P1, P2, R> was expected"));
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
index 2196586..d85c992 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
@@ -5,8 +5,16 @@
import com.android.tools.r8.kotlin.metadata.extension_function_lib.B
import com.android.tools.r8.kotlin.metadata.extension_function_lib.extension
+import com.android.tools.r8.kotlin.metadata.extension_function_lib.csHash
+import com.android.tools.r8.kotlin.metadata.extension_function_lib.longArrayHash
+import com.android.tools.r8.kotlin.metadata.extension_function_lib.myApply
fun main() {
B().doStuff()
B().extension()
+
+ "R8".csHash()
+ longArrayOf(42L).longArrayHash()
+ // TODO(b/70169921): Need to set arguments as type parameter.
+ // B().myApply { this.doStuff() }
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_lib/B.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_lib/B.kt
index 84c759a..4321ac1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_lib/B.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_lib/B.kt
@@ -17,4 +17,20 @@
fun B.extension() {
doStuff()
-}
\ No newline at end of file
+}
+
+fun CharSequence.csHash(): Long {
+ var result = 0L
+ this.forEach { result = result * 8L + it.toLong() }
+ return result
+}
+
+fun LongArray.longArrayHash(): Long {
+ var result = 0L
+ this.forEach { result = result * 8L + it }
+ return result
+}
+
+fun B.myApply(apply: B.() -> Unit) {
+ apply.invoke(this)
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java
index e869207..c72d305 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentKmFunctionSubject.java
@@ -31,4 +31,14 @@
public JvmMethodSignature signature() {
return null;
}
+
+ @Override
+ public KmTypeSubject receiverParameterType() {
+ return null;
+ }
+
+ @Override
+ public KmTypeSubject returnType() {
+ return null;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
index f99ac6c..7b57397 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
@@ -63,7 +63,7 @@
@Override
public List<String> getSuperTypeDescriptors() {
return kmClass.getSupertypes().stream()
- .map(this::getDescriptorFromKmType)
+ .map(KmTypeSubject::getDescriptorFromKmType)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
index 9fece50..b9fa99a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmDeclarationContainerSubject.java
@@ -3,10 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.codeinspector;
-import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
+import static com.android.tools.r8.utils.codeinspector.KmTypeSubject.getDescriptorFromKmType;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.Box;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -19,7 +18,6 @@
import kotlinx.metadata.KmPropertyExtensionVisitor;
import kotlinx.metadata.KmPropertyVisitor;
import kotlinx.metadata.KmType;
-import kotlinx.metadata.KmTypeVisitor;
import kotlinx.metadata.jvm.JvmFieldSignature;
import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor;
import kotlinx.metadata.jvm.JvmMethodSignature;
@@ -30,26 +28,6 @@
CodeInspector codeInspector();
KmDeclarationContainer getKmDeclarationContainer();
- // TODO(b/145824437): This is a dup of DescriptorUtils#getDescriptorFromKmType
- default String getDescriptorFromKmType(KmType kmType) {
- if (kmType == null) {
- return null;
- }
- Box<String> descriptor = new Box<>(null);
- kmType.accept(new KmTypeVisitor() {
- @Override
- public void visitClass(String name) {
- descriptor.set(getDescriptorFromKotlinClassifier(name));
- }
-
- @Override
- public void visitTypeAlias(String name) {
- descriptor.set(getDescriptorFromKotlinClassifier(name));
- }
- });
- return descriptor.get();
- }
-
@Override
default List<String> getParameterTypeDescriptorsInFunctions() {
return getKmDeclarationContainer().getFunctions().stream()
@@ -97,6 +75,8 @@
};
}
});
+ // We don't check Kotlin types in tests, but be aware of the relocation issue.
+ // See b/70169921#comment57 for more details.
}
}
@@ -202,6 +182,8 @@
};
}
});
+ // We don't check Kotlin types in tests, but be aware of the relocation issue.
+ // See b/70169921#comment57 for more details.
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
index 7c970c2..3bbf235 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmFunctionSubject.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.utils.codeinspector.FoundKmDeclarationContainerSubject.KmFunctionProcessor;
import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmType;
import kotlinx.metadata.jvm.JvmMethodSignature;
public class FoundKmFunctionSubject extends KmFunctionSubject {
@@ -46,4 +47,16 @@
public JvmMethodSignature signature() {
return signature;
}
+
+ @Override
+ public KmTypeSubject receiverParameterType() {
+ KmType kmType = kmFunction.getReceiverParameterType();
+ assert !isExtension() || kmType != null;
+ return kmType == null ? null : new KmTypeSubject(kmType);
+ }
+
+ @Override
+ public KmTypeSubject returnType() {
+ return new KmTypeSubject(kmFunction.getReturnType());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java
index 2e9eeda..d0e0a37 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmFunctionSubject.java
@@ -15,4 +15,8 @@
public abstract boolean isExtension();
public abstract JvmMethodSignature signature();
+
+ public abstract KmTypeSubject receiverParameterType();
+
+ public abstract KmTypeSubject returnType();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
new file mode 100644
index 0000000..aa52c67
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
@@ -0,0 +1,63 @@
+// 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.utils.codeinspector;
+
+import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.Box;
+import kotlinx.metadata.KmType;
+import kotlinx.metadata.KmTypeVisitor;
+
+public class KmTypeSubject extends Subject {
+ private final KmType kmType;
+
+ KmTypeSubject(KmType kmType) {
+ assert kmType != null;
+ this.kmType = kmType;
+ }
+
+ // TODO(b/145824437): This is a dup of DescriptorUtils#getDescriptorFromKmType
+ static String getDescriptorFromKmType(KmType kmType) {
+ if (kmType == null) {
+ return null;
+ }
+ Box<String> descriptor = new Box<>(null);
+ kmType.accept(new KmTypeVisitor() {
+ @Override
+ public void visitClass(String name) {
+ // We don't check Kotlin types in tests, but be aware of the relocation issue.
+ // See b/70169921#comment25 for more details.
+ assert descriptor.get() == null;
+ descriptor.set(getDescriptorFromKotlinClassifier(name));
+ }
+
+ @Override
+ public void visitTypeAlias(String name) {
+ assert descriptor.get() == null;
+ descriptor.set(getDescriptorFromKotlinClassifier(name));
+ }
+ });
+ return descriptor.get();
+ }
+
+ public String descriptor() {
+ return getDescriptorFromKmType(kmType);
+ }
+
+ @Override
+ public boolean isPresent() {
+ return true;
+ }
+
+ @Override
+ public boolean isRenamed() {
+ throw new Unreachable("Cannot determine if a type is renamed");
+ }
+
+ @Override
+ public boolean isSynthetic() {
+ throw new Unreachable("Cannot determine if a type is synthetic");
+ }
+}