Allow R class fields that don't have a value
We saw issues with this in coverage runs in platform
Bug: 287398085
Change-Id: I554c3d5820ae01f1b309a98ea43f72b1c7245497
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ResourceAccessAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ResourceAccessAnalysis.java
index b39eb30..01e5f1e 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ResourceAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ResourceAccessAnalysis.java
@@ -80,8 +80,12 @@
assert fieldToValueMapping.containsKey(holderType);
RClassFieldToValueStore rClassFieldToValueStore = fieldToValueMapping.get(holderType);
IntList integers = rClassFieldToValueStore.valueMapping.get(field);
- for (Integer integer : integers) {
- resourceShrinkerState.trace(integer);
+ // The R class can have fields injected, e.g., by jacoco, we don't have resource values for
+ // these.
+ if (integers != null) {
+ for (Integer integer : integers) {
+ resourceShrinkerState.trace(integer);
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/androidresources/RClassWithNonIDFieldTest.java b/src/test/java/com/android/tools/r8/androidresources/RClassWithNonIDFieldTest.java
new file mode 100644
index 0000000..8cac122
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/RClassWithNonIDFieldTest.java
@@ -0,0 +1,80 @@
+// 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 com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+import com.google.common.collect.ImmutableList;
+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 RClassWithNonIDFieldTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withDefaultDexRuntime().withAllApiLevels().build();
+ }
+
+ public static AndroidTestResource getTestResources(TemporaryFolder temp) throws Exception {
+ return new AndroidTestResourceBuilder()
+ .withSimpleManifestAndAppNameString()
+ .addRClassInitializeWithDefaultValues(R.string.class)
+ .build(temp);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // We test the non id field type by simply passing the standard test resources but ignoring
+ // the aapt generated R class, instead we pass directly the R class from this file, which
+ // have no real resource references, but does have a non integer field.
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(FooBar.class)
+ .addAndroidResources(
+ getTestResources(temp),
+ temp.newFile("resout.zip").toPath(),
+ ImmutableList.of(ToolHelper.getClassAsBytes(R.string.class)))
+ .addKeepMainRule(FooBar.class)
+ .enableOptimizedShrinking()
+ .compile()
+ .inspectShrunkenResources(
+ resourceTableInspector -> {
+ // We explicitly do not have any resources traced since we don't use the aapt
+ // R class file.
+ resourceTableInspector.assertDoesNotContainResourceWithName("string", "foo");
+ })
+ .run(parameters.getRuntime(), FooBar.class)
+ .assertSuccessWithOutputLines("bar");
+ }
+
+ public static class FooBar {
+ public static void main(String[] args) {
+ if (System.currentTimeMillis() == 0) {
+ System.out.println(R.string.foo);
+ }
+ if (R.string.nonResource != null) {
+ System.out.println("bar");
+ }
+ }
+ }
+
+ public static class R {
+ public static class string {
+ private static Object nonResource = new Object();
+ public static int foo = 0x7f110004;
+ }
+ }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
index a9c431a..5f68743 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
@@ -3,9 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import static com.android.tools.r8.dexsplitter.SplitterTestBase.simpleSplitProvider;
-import static com.android.tools.r8.dexsplitter.SplitterTestBase.splitWithNonJavaFile;
-import static com.android.tools.r8.utils.codeinspector.Matchers.proguardConfigurationRuleDoesNotMatch;
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
@@ -945,6 +942,12 @@
}
public T addAndroidResources(AndroidTestResource testResource, Path output) throws IOException {
+ List<byte[]> classFileData = testResource.getRClass().getClassFileData();
+ return addAndroidResources(testResource, output, classFileData);
+ }
+
+ public T addAndroidResources(
+ AndroidTestResource testResource, Path output, List<byte[]> classFileData) {
Path resources = testResource.getResourceZip();
resourceShrinkerOutput = output;
getBuilder()
@@ -952,7 +955,8 @@
new ArchiveProtoAndroidResourceProvider(resources, new PathOrigin(resources)));
getBuilder()
.setAndroidResourceConsumer(new ArchiveProtoAndroidResourceConsumer(output, resources));
- return addProgramClassFileData(testResource.getRClass().getClassFileData());
+
+ return addProgramClassFileData(classFileData);
}
public T setAndroidResourcesFromPath(Path input) throws IOException {