Read Kotlin @Metadata after 1st tree shaking.
Bug: 70169921, 148654451
Change-Id: I39223a81df9e3ce5098d33be2c06a4698c1ad24c
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7db3d7c..ecf6749 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -306,10 +306,6 @@
}
}
- // Compute kotlin info before setting the roots and before
- // kotlin metadata annotation is removed.
- computeKotlinInfoForProgramClasses(application, appView);
-
// Add synthesized -assumenosideeffects from min api if relevant.
if (options.isGeneratingDex()) {
if (!ProguardConfigurationUtils.hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
@@ -399,6 +395,22 @@
appView.appInfo().unsetObsolete();
}
+ // Compute Kotlin info after the first round of tree shaking. With this quasi-lazy loading,
+ // we only read Kotlin @Metadata associated with _live_ program classes.
+ //
+ // One caveat is, Kotlin @Metadata annotation would be freely removed according to general
+ // logic in {@link AnnotationRemover}. To properly parse annotations, one needs a rule like:
+ // -keep class kotlin.Metadata { *; }
+ // not like:
+ // -keep class kotlin.Metadata
+ // which specifies to keep Metadata, but its members, e.g., kind, d1, d2, etc., are still
+ // allowed to be shrunken.
+ //
+ // We can't postpone it further, though, since the following structural optimizations, such
+ // as vertical class merging, can migrate, e.g., methods from one class to its sub-class or
+ // implementer while the corresponding (member-level) @Metadata is not yet read.
+ computeKotlinInfoForProgramClasses(application, appView);
+
// The class type lattice elements include information about the interfaces that a class
// implements. This information can change as a result of vertical class merging, so we need
// to clear the cache, so that we will recompute the type lattice elements.
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
index 6bfafc4..fd89b90 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
@@ -12,6 +12,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
@@ -24,6 +25,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import org.hamcrest.core.StringContains;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -100,8 +102,8 @@
.addProgramFiles(annoJarMap.get(targetVersion), ToolHelper.getKotlinStdlibJar())
.addProgramFiles(inputJarMap.get(targetVersion))
.addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT)
- .addKeepRules("-keep class **.Anno")
- .addKeepRules("-keep class kotlin.Metadata")
+ .addKeepRules("-keep class **.Anno { *; }")
+ .addKeepRules("-keep class kotlin.Metadata { *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
.compile()
@@ -110,6 +112,35 @@
.inspect(this::inspect);
}
+ @Test
+ public void testR8_kotlinStdlibAsProgramFile_insufficientRule() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(annoJarMap.get(targetVersion), ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(inputJarMap.get(targetVersion))
+ .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT)
+ .addKeepRules("-keep class **.Anno { *; }")
+ .addKeepRules("-keep class kotlin.Metadata { *** d1; }")
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .allowDiagnosticMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertErrorsCount(0);
+ diagnostics.assertInfosCount(4);
+ for (Diagnostic diagnostic : diagnostics.getInfos()) {
+ assertThat(
+ diagnostic.getDiagnosticMessage(),
+ StringContains.containsString(
+ "has malformed kotlin.Metadata: element 'k' is missing."));
+ }
+ diagnostics.assertWarningsCount(3);
+ for (Diagnostic diagnostic : diagnostics.getWarnings()) {
+ assertThat(
+ diagnostic.getDiagnosticMessage(),
+ StringContains.containsString("Resource 'META-INF/"));
+ }
+ });
+ }
+
private void inspect(CodeInspector inspector) {
String pkg = getClass().getPackage().getName();
ClassSubject kept = inspector.clazz(pkg + ".anno.Kept");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 31cd07d..2f2d3dc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -51,7 +51,7 @@
.addProgramFiles(ToolHelper.getKotlinReflectJar())
.addKeepMainRule(mainClassName)
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
- .addKeepRules("-keep class kotlin.Metadata")
+ .addKeepRules("-keep class kotlin.Metadata { *; }")
// TODO(b/70169921): if this option is settled down, this test is meaningless.
.addOptionsModification(o -> o.enableKotlinMetadataRewritingForRenamedClasses = false)
.allowDiagnosticWarningMessages()