Null out names of unused resources in optimized shrinking
These are not needed when the resource can no longer be looked up.
Bug: 287398085
Change-Id: Ibb13cb1dbe6da8bc90f5aae374bcb469fbc2e0c2
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/ResourceTableUtil.kt b/src/resourceshrinker/java/com/android/build/shrinker/ResourceTableUtil.kt
index 4327997..88bb5e4 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/ResourceTableUtil.kt
+++ b/src/resourceshrinker/java/com/android/build/shrinker/ResourceTableUtil.kt
@@ -33,20 +33,28 @@
internal fun Resources.ResourceTable.nullOutEntriesWithIds(ids: List<Int>)
: Resources.ResourceTable {
+ return nullOutEntriesWithIds(ids, false)
+}
+
+internal fun Resources.ResourceTable.nullOutEntriesWithIds(
+ ids: List<Int>, pruneResourceNames: Boolean): Resources.ResourceTable {
if (ids.isEmpty()) {
return this
}
val packageMappings = calculatePackageMappings(ids)
val tableBuilder = this.toBuilder()
- tableBuilder.packageBuilderList.forEach{
+ tableBuilder.packageBuilderList.forEach {
val typeMappings = packageMappings[it.packageId.id]
if (typeMappings != null) {
- it.typeBuilderList.forEach { type->
+ it.typeBuilderList.forEach { type ->
val entryList = typeMappings[type.typeId.id]
if (entryList != null) {
type.entryBuilderList.forEach { entry ->
if (entryList.contains(entry.entryId.id)) {
entry.clearConfigValue()
+ if (pruneResourceNames) {
+ entry.clearName();
+ }
if (entry.hasOverlayableItem()) {
entry.clearOverlayableItem()
}
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
index 11bb767..29a3399 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
+++ b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
@@ -177,7 +177,8 @@
(resourceTable, featureSplit) ->
shrunkenTables.put(
featureSplit,
- ResourceTableUtilKt.nullOutEntriesWithIds(resourceTable, resourceIdsToRemove)));
+ ResourceTableUtilKt.nullOutEntriesWithIds(
+ resourceTable, resourceIdsToRemove, true)));
return new ShrinkerResult(resEntriesToKeep, shrunkenTables);
}
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 8237f9b..7454ce3 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertNotNull;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils;
import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.ResourceTableInspector;
import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
import com.android.tools.r8.profile.art.model.ExternalArtProfile;
@@ -173,6 +174,12 @@
return self();
}
+ public String dumpResources() throws IOException {
+ ProcessResult processResult = AndroidResourceTestingUtils.dumpWithAapt2(resourceShrinkerOutput);
+ assert processResult.exitCode == 0;
+ return processResult.stdout;
+ }
+
public <E extends Throwable> R8TestCompileResult inspectShrunkenResourcesForFeature(
Consumer<ResourceTableInspector> consumer, String featureName) throws IOException {
Path path = resourceShrinkerOutputForFeatures.get(featureName);
diff --git a/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java b/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
index b4a170a..df7461e 100644
--- a/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
@@ -532,8 +532,12 @@
}
}
- public static void dumpWithAapt2(Path path) throws IOException {
- System.out.println(ToolHelper.runAapt2("dump", "resources", path.toString()));
+ public static ProcessResult dumpWithAapt2(Path path) throws IOException {
+ return ToolHelper.runAapt2("dump", "resources", path.toString());
+ }
+
+ public static void dumpWithAapt2ToStdout(Path path) throws IOException {
+ System.out.println(dumpWithAapt2(path));
}
public static void compileWithAapt2(
diff --git a/src/test/java/com/android/tools/r8/androidresources/TestNameRemovalInResourceTable.java b/src/test/java/com/android/tools/r8/androidresources/TestNameRemovalInResourceTable.java
new file mode 100644
index 0000000..b9d73a0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/TestNameRemovalInResourceTable.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2024, 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.androidresources;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TestNameRemovalInResourceTable extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean optimized;
+
+ @Parameters(name = "{0}, optimized: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDefaultDexRuntime().withAllApiLevels().build(),
+ BooleanUtils.values());
+ }
+
+ public static AndroidTestResource getTestResources(TemporaryFolder temp) throws Exception {
+ return new AndroidTestResourceBuilder()
+ .withSimpleManifestAndAppNameString()
+ .addRClassInitializeWithDefaultValues(R.string.class, R.drawable.class)
+ .build(temp);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestCompileResult r8TestCompileResult =
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(FooBar.class)
+ .addAndroidResources(getTestResources(temp))
+ .applyIf(optimized, R8TestBuilder::enableOptimizedShrinking)
+ .addKeepMainRule(FooBar.class)
+ .compile();
+ r8TestCompileResult
+ .inspectShrunkenResources(
+ resourceTableInspector -> {
+ resourceTableInspector.assertContainsResourceWithName("string", "bar");
+ resourceTableInspector.assertContainsResourceWithName("string", "foo");
+ resourceTableInspector.assertContainsResourceWithName("drawable", "foobar");
+ resourceTableInspector.assertDoesNotContainResourceWithName(
+ "string", "unused_string");
+ resourceTableInspector.assertDoesNotContainResourceWithName(
+ "drawable", "unused_drawable");
+ })
+ .run(parameters.getRuntime(), FooBar.class)
+ .assertSuccess();
+ String dumpResources = r8TestCompileResult.dumpResources();
+ if (optimized) {
+ assertFalse(dumpResources.contains("unused_string"));
+ assertFalse(dumpResources.contains("unused_drawable"));
+ } else {
+ assertTrue(dumpResources.contains("unused_string"));
+ assertTrue(dumpResources.contains("unused_drawable"));
+ }
+ }
+
+ public static class FooBar {
+
+ public static void main(String[] args) {
+ if (System.currentTimeMillis() == 0) {
+ System.out.println(R.drawable.foobar);
+ System.out.println(R.string.bar);
+ System.out.println(R.string.foo);
+ }
+ }
+ }
+
+ public static class R {
+
+ public static class string {
+
+ public static int bar;
+ public static int foo;
+ public static int unused_string;
+ }
+
+ public static class drawable {
+
+ public static int foobar;
+ public static int unused_drawable;
+ }
+ }
+}