Do resourcestore lookups using R class based names
The resource model will rewrite names to match the R class names if they contain dots
This is conservative, in that we will potentially match resources not
reachable (actual resource string/foo_bar, string constant foo.bar)
I will apply this in AGP as well, but the regression test is much easier to write in our setup
Bug: b/309078004
Change-Id: I638a3bdb9c06e82ee79380d78be5ff601cc40f92
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/PossibleResourcesMarker.java b/src/resourceshrinker/java/com/android/build/shrinker/PossibleResourcesMarker.java
index 5089061..b710916 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/PossibleResourcesMarker.java
+++ b/src/resourceshrinker/java/com/android/build/shrinker/PossibleResourcesMarker.java
@@ -16,6 +16,7 @@
package com.android.build.shrinker;
+import static com.android.ide.common.resources.ResourcesUtil.resourceNameToFieldName;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.lang.Character.isDigit;
@@ -122,7 +123,7 @@
haveSlash |= c == '/';
formatting |= c == '%';
justName =
- justName && !(c == '.' || c == ':' || c == '%' || c == '/');
+ justName && !(c == ':' || c == '%' || c == '/');
}
Stream<Resource> reachable = Streams.concat(
@@ -170,7 +171,7 @@
// Check for a simple prefix match, e.g. as in
// getResources().getIdentifier("ic_video_codec_" + codecName, "drawable", ...)
return resourceStore.getResources().stream()
- .filter(resource -> resource.name.startsWith(string));
+ .filter(resource -> resource.name.startsWith(resourceNameToFieldName(string)));
}
private Stream<Resource> possibleFormatting(String string) {
@@ -191,7 +192,7 @@
// Try to pick out the resource name pieces; if we can find the
// resource type unambiguously; if not, just match on names
int slash = string.indexOf('/');
- String name = string.substring(slash + 1);
+ String name = resourceNameToFieldName(string.substring(slash + 1));
if (name.isEmpty() || !names.contains(name)) {
return Stream.empty();
}
diff --git a/src/test/java/com/android/tools/r8/androidresources/ResourceNameWithDotsRegressionTest.java b/src/test/java/com/android/tools/r8/androidresources/ResourceNameWithDotsRegressionTest.java
new file mode 100644
index 0000000..1e28ef0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/ResourceNameWithDotsRegressionTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2023, 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 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.nio.file.Path;
+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 ResourceNameWithDotsRegressionTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean debug;
+
+ @Parameter(2)
+ public boolean optimized;
+
+ @Parameters(name = "{0}, debug: {1}, optimized: {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDefaultDexRuntime().withAllApiLevels().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values());
+ }
+
+ public static AndroidTestResource getTestResources(TemporaryFolder temp) throws Exception {
+ return new AndroidTestResourceBuilder()
+ .withSimpleManifestAndAppNameString()
+ .addStringValue("foo.bar", "the foobar string")
+ .build(temp);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ AndroidTestResource testResources = getTestResources(temp);
+ Path resourcesClass = AndroidResourceTestingUtils.resourcesClassAsDex(temp);
+ byte[] withAnroidResourcesReference =
+ AndroidResourceTestingUtils.transformResourcesReferences(FooBar.class);
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClassFileData(withAnroidResourcesReference)
+ .addRunClasspathFiles(resourcesClass)
+ .addAndroidResources(testResources)
+ .addKeepMainRule(FooBar.class)
+ .compile()
+ .inspectShrunkenResources(
+ resourceTableInspector -> {
+ resourceTableInspector.assertContainsResourceWithName("string", "foo.bar");
+ })
+ .run(parameters.getRuntime(), FooBar.class)
+ .assertSuccess();
+ }
+
+ public static class FooBar {
+
+ public static void main(String[] args) {
+ if (System.currentTimeMillis() == 0) {
+ Resources resources = new Resources();
+ resources.getIdentifier("foo.bar", "string", "mypackage");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/androidresources/Resources.java b/src/test/java/com/android/tools/r8/androidresources/Resources.java
index ef01a99..e702f2a 100644
--- a/src/test/java/com/android/tools/r8/androidresources/Resources.java
+++ b/src/test/java/com/android/tools/r8/androidresources/Resources.java
@@ -13,4 +13,8 @@
public String getString(int id) {
return GET_STRING_VALUE;
}
+
+ public int getIdentifier(String name, String defType, String defPackage) {
+ return 42;
+ }
}