[Metadata] Keep track of companion object name in KotlinClassInfo
Bug: b/248450861
Change-Id: Icbe99516fa323f427f7b6926b63a8385698b5e2f
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index 9ddddfe..100d61a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -54,6 +54,7 @@
private final String inlineClassUnderlyingPropertyName;
private final KotlinTypeInfo inlineClassUnderlyingType;
private final int jvmFlags;
+ private final String companionObjectName;
// List of tracked assignments of kotlin metadata.
private final KotlinMetadataMembersTracker originalMembersWithKotlinInfo;
@@ -78,7 +79,8 @@
String inlineClassUnderlyingPropertyName,
KotlinTypeInfo inlineClassUnderlyingType,
KotlinMetadataMembersTracker originalMembersWithKotlinInfo,
- int jvmFlags) {
+ int jvmFlags,
+ String companionObjectName) {
this.flags = flags;
this.name = name;
this.nameCanBeSynthesizedFromClassOrAnonymousObjectOrigin =
@@ -100,6 +102,7 @@
this.inlineClassUnderlyingType = inlineClassUnderlyingType;
this.originalMembersWithKotlinInfo = originalMembersWithKotlinInfo;
this.jvmFlags = jvmFlags;
+ this.companionObjectName = companionObjectName;
}
public static KotlinClassInfo create(
@@ -154,7 +157,7 @@
keepByteCode,
extensionInformation,
originalMembersWithKotlinInfo);
- setCompanionObject(kmClass, hostClass, reporter);
+ String companionObjectName = setCompanionObject(kmClass, hostClass, reporter);
KotlinTypeReference anonymousObjectOrigin = getAnonymousObjectOrigin(kmClass, factory);
boolean nameCanBeDeducedFromClassOrOrigin =
kmClass.name.equals(
@@ -183,7 +186,8 @@
kmClass.getInlineClassUnderlyingPropertyName(),
KotlinTypeInfo.create(kmClass.getInlineClassUnderlyingType(), factory, reporter),
originalMembersWithKotlinInfo,
- JvmExtensionsKt.getJvmFlags(kmClass));
+ JvmExtensionsKt.getJvmFlags(kmClass),
+ companionObjectName);
}
private static KotlinTypeReference getAnonymousObjectOrigin(
@@ -228,19 +232,20 @@
return superTypeInfos.build();
}
- private static void setCompanionObject(KmClass kmClass, DexClass hostClass, Reporter reporter) {
+ private static String setCompanionObject(KmClass kmClass, DexClass hostClass, Reporter reporter) {
String companionObjectName = kmClass.getCompanionObject();
if (companionObjectName == null) {
- return;
+ return companionObjectName;
}
for (DexEncodedField field : hostClass.fields()) {
if (field.getReference().name.toString().equals(companionObjectName)) {
field.setKotlinMemberInfo(new KotlinCompanionInfo(companionObjectName));
- return;
+ return companionObjectName;
}
}
reporter.warning(
KotlinMetadataDiagnostic.missingCompanionObject(hostClass, companionObjectName));
+ return companionObjectName;
}
@Override
@@ -284,6 +289,7 @@
rewritten |= !name.equals(rewrittenName);
}
// Find a companion object.
+ boolean foundCompanion = false;
for (DexEncodedField field : clazz.fields()) {
if (field.getKotlinInfo().isCompanion()) {
rewritten |=
@@ -291,8 +297,14 @@
.getKotlinInfo()
.asCompanion()
.rewrite(kmClass, field.getReference(), appView.getNamingLens());
+ foundCompanion = true;
}
}
+ // If we did not find a companion but it was there on input we have to emit a new metadata
+ // object.
+ if (!foundCompanion && companionObjectName != null) {
+ rewritten = true;
+ }
// Take all not backed constructors because we will never find them in definitions.
for (KotlinConstructorInfo constructorInfo : constructorsWithNoBacking) {
rewritten |= constructorInfo.rewrite(kmClass, null, appView);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRemovedCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRemovedCompanionTest.java
index 2f27bf0..e4f94f6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRemovedCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteRemovedCompanionTest.java
@@ -4,10 +4,7 @@
package com.android.tools.r8.kotlin.metadata;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertThrows;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.StringUtils;
@@ -89,23 +86,29 @@
}
@Test
- public void testMetadataInCompanion_removedField() {
- // TODO(b/248450861): Should not throw.
- assertThrows(
- CompilationFailedException.class,
- () ->
- testForR8(parameters.getBackend())
- .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
- .addProgramFiles(companionRemoveJarMap.getForConfiguration(kotlinc, targetVersion))
- // Keep the B class and its interface (which has the doStuff method).
- .addKeepKotlinMetadata()
- .addKeepRules(
- "-keep class **.ClassWithCompanion { void <init>(); void doStuff(); }")
- .addKeepRules("-keep class **.ClassWithCompanion$Companion")
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorMessageThatMatches(
- containsString("The metadata should be equivalent"));
- }));
+ public void testMetadataInCompanion_removedField() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
+ .addProgramFiles(companionRemoveJarMap.getForConfiguration(kotlinc, targetVersion))
+ // Keep the ClasWithCompanion class.
+ .addKeepKotlinMetadata()
+ .addKeepRules("-keep class **.ClassWithCompanion { void <init>(); void doStuff(); }")
+ .addKeepRules("-keep class **.ClassWithCompanion$Companion")
+ .compile()
+ .writeToZip();
+
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_remove_app", "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".companion_remove_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
}
}