Towards synthesizing Kotlin @Metadata: sealedSubclasses in KmClass.
Bug: 70169921
Change-Id: Ifb9e36bc06d40fbbb6526d53917f4e0b8d878424
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 861da28..0edb554 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -6,8 +6,10 @@
import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toKmType;
+import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedClassifier;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmConstructor;
import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toRenamedKmType;
+import static kotlinx.metadata.Flag.IS_SEALED;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -81,7 +83,16 @@
rewriteDeclarationContainer(kmClass, appView, lens);
- // TODO(b/70169921): companion
+ List<String> sealedSubclasses = kmClass.getSealedSubclasses();
+ sealedSubclasses.clear();
+ if (IS_SEALED.invoke(kmClass.getFlags())) {
+ for (DexType subtype : appView.appInfo().allImmediateSubtypes(clazz.type)) {
+ String classifier = toRenamedClassifier(subtype, appView, lens);
+ if (classifier != null) {
+ sealedSubclasses.add(classifier);
+ }
+ }
+ }
}
@Override
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 5ce0e97..8a8773a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
@@ -3,11 +3,11 @@
// 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 static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature;
import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
-import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKmType;
+import static com.android.tools.r8.kotlin.Kotlin.NAME;
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
import static kotlinx.metadata.FlagsKt.flagsOf;
import com.android.tools.r8.graph.AppView;
@@ -40,23 +40,21 @@
static KmType toKmType(String descriptor) {
KmType kmType = new KmType(flagsOf());
- kmType.visitClass(descriptorToInternalName(descriptor));
+ kmType.visitClass(descriptorToKotlinClassifier(descriptor));
return kmType;
}
- static KmType toRenamedKmType(
+ static String toRenamedClassifier(
DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
- // E.g., [Ljava/lang/String; -> Lkotlin/Array;
+ // E.g., [Ljava/lang/String; -> kotlin/Array
if (type.isArrayType()) {
- return toKmType(addKotlinPrefix("Array;"));
+ return NAME + "/Array";
}
- // E.g., void -> Lkotlin/Unit;
+ // E.g., void -> kotlin/Unit
if (appView.dexItemFactory().kotlin.knownTypeConversion.containsKey(type)) {
- KmType kmType = new KmType(flagsOf());
DexType convertedType = appView.dexItemFactory().kotlin.knownTypeConversion.get(type);
assert convertedType != null;
- kmType.visitClass(descriptorToInternalName(convertedType.toDescriptorString()));
- return kmType;
+ return descriptorToKotlinClassifier(convertedType.toDescriptorString());
}
// For library or classpath class, synthesize @Metadata always.
// For a program class, make sure it is live.
@@ -68,10 +66,19 @@
DexClass clazz = appView.definitionFor(type);
assert clazz == null || clazz.isProgramClass() || renamedType == type
: type.toSourceString() + " -> " + renamedType.toSourceString();
+ return descriptorToKotlinClassifier(renamedType.toDescriptorString());
+ }
+
+ static KmType toRenamedKmType(
+ DexType type, AppView<AppInfoWithLiveness> appView, NamingLens lens) {
+ String classifier = toRenamedClassifier(type, appView, lens);
+ if (classifier == null) {
+ return null;
+ }
// TODO(b/70169921): Mysterious, why attempts to properly set flags bothers kotlinc?
// and/or why wiping out flags works for KmType but not KmFunction?!
KmType kmType = new KmType(flagsOf());
- kmType.visitClass(descriptorToInternalName(renamedType.toDescriptorString()));
+ kmType.visitClass(classifier);
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 fd51973..70d6df0 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -178,6 +178,16 @@
}
/**
+ * Convert a descriptor to a classifier in Kotlin metadata
+ * @param descriptor like "Lorg/foo/bar/Baz$Nested;"
+ * @return className "org/foo/bar/Baz.Nested"
+ */
+ public static String descriptorToKotlinClassifier(String descriptor) {
+ return getBinaryNameFromDescriptor(descriptor)
+ .replace(INNER_CLASS_SEPARATOR, JAVA_PACKAGE_SEPARATOR);
+ }
+
+ /**
* Convert a type descriptor to a Java type name. Will also deobfuscate class names if a
* class mapper is provided.
*
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java
index 725576d..a2d4fa1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRenameInSealedClassTest.java
@@ -11,8 +11,9 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
@@ -90,10 +91,8 @@
kmClass.getSealedSubclassDescriptors().forEach(sealedSubclassDescriptor -> {
ClassSubject sealedSubclass =
inspector.clazz(descriptorToJavaType(sealedSubclassDescriptor));
- // TODO(b/70169921): sealedSubclasses in KmClass should refer to live classes.
- // assertThat(sealedSubclass, isRenamed());
- // TODO(b/70169921): sealedSubclasses in KmClass should refer to renamed classes.
- assertNotEquals(sealedSubclassDescriptor, sealedSubclass.getFinalDescriptor());
+ assertThat(sealedSubclass, isRenamed());
+ assertEquals(sealedSubclassDescriptor, sealedSubclass.getFinalDescriptor());
});
ClassSubject libKt = inspector.clazz(libClassName);
@@ -153,8 +152,7 @@
KmClassSubject kmClass = expr.getKmClass();
assertThat(kmClass, isPresent());
- // TODO(b/70169921): Then, we can cleanup sealedSubclasses in KmClass
- assertFalse(kmClass.getSealedSubclassDescriptors().isEmpty());
+ assertTrue(kmClass.getSealedSubclassDescriptors().isEmpty());
ClassSubject libKt = inspector.clazz(libClassName);
assertThat(expr, isPresent());