Merge commit '7d3ce88195d3a032449ea05fa906a96a619d7e2f' into dev-release
Change-Id: I385652421e0b66a696d6a02e055ff2870a0e386f
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 14aa18a..5e52e33 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
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueResourceNumber;
import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
@@ -100,7 +101,7 @@
if (enqueuer.isRClass(resolvedField.getHolder())) {
DexType holderType = resolvedField.getHolderType();
if (!fieldToValueMapping.containsKey(holderType)) {
- populateRClassValues(resolvedField);
+ populateRClassValues(resolvedField.getHolder());
}
assert fieldToValueMapping.containsKey(holderType);
RClassFieldToValueStore rClassFieldToValueStore = fieldToValueMapping.get(holderType);
@@ -115,17 +116,17 @@
}
}
- private void populateRClassValues(ProgramField field) {
+ private void populateRClassValues(DexProgramClass programClass) {
// TODO(287398085): Pending discussions with the AAPT2 team, we might need to harden this
// to not fail if we wrongly classify an unrelated class as R class in our heuristic..
RClassFieldToValueStore.Builder rClassValueBuilder = new RClassFieldToValueStore.Builder();
- analyzeStaticFields(field, rClassValueBuilder);
- ProgramMethod programClassInitializer = field.getHolder().getProgramClassInitializer();
+ analyzeStaticFields(programClass, rClassValueBuilder);
+ ProgramMethod programClassInitializer = programClass.getProgramClassInitializer();
if (programClassInitializer != null) {
analyzeClassInitializer(rClassValueBuilder, programClassInitializer);
}
- warnOnFinalIdFields(field.getHolder());
- fieldToValueMapping.put(field.getHolderType(), rClassValueBuilder.build());
+ warnOnFinalIdFields(programClass);
+ fieldToValueMapping.put(programClass.getType(), rClassValueBuilder.build());
}
private void warnOnFinalIdFields(DexProgramClass holder) {
@@ -201,13 +202,15 @@
}
private void analyzeStaticFields(
- ProgramField field, RClassFieldToValueStore.Builder rClassValueBuilder) {
+ DexProgramClass programClass, RClassFieldToValueStore.Builder rClassValueBuilder) {
for (DexEncodedField staticField :
- field.getHolder().staticFields(DexEncodedField::hasExplicitStaticValue)) {
+ programClass.staticFields(DexEncodedField::hasExplicitStaticValue)) {
DexValue staticValue = staticField.getStaticValue();
if (staticValue.isDexValueInt()) {
IntList values = new IntArrayList(1);
values.add(staticValue.asDexValueInt().getValue());
+ staticField.setStaticValue(
+ DexValueResourceNumber.create(staticValue.asDexValueInt().value));
rClassValueBuilder.addMapping(staticField.getReference(), values);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 20db4fc..30d420d 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3280,11 +3280,6 @@
// Already live.
return;
}
-
- assert !field.getAccessFlags().isStatic()
- || !field.getDefinition().hasExplicitStaticValue()
- || !field.getDefinition().getStaticValue().isDexValueResourceNumber()
- || mode.isFinalTreeShaking();
addEffectivelyLiveOriginalField(field);
// Mark the field as targeted.
diff --git a/src/test/java/com/android/tools/r8/androidresources/NonFinalRClassFieldsWithStaticNonCLInitValueTest.java b/src/test/java/com/android/tools/r8/androidresources/NonFinalRClassFieldsWithStaticNonCLInitValueTest.java
new file mode 100644
index 0000000..4590f8b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/androidresources/NonFinalRClassFieldsWithStaticNonCLInitValueTest.java
@@ -0,0 +1,67 @@
+// 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.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
+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 NonFinalRClassFieldsWithStaticNonCLInitValueTest 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.drawable.class)
+ // Use static value integers for the fields (i.e., no <clinit>).
+ .useStaticValueForNonFinalFields()
+ .build(temp);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(FooBar.class)
+ .addAndroidResources(getTestResources(temp))
+ .addKeepMainRule(FooBar.class)
+ .enableOptimizedShrinking()
+ .compile()
+ .inspectShrunkenResources(
+ resourceTableInspector -> {
+ resourceTableInspector.assertContainsResourceWithName("drawable", "foo");
+ resourceTableInspector.assertDoesNotContainResourceWithName("drawable", "bar");
+ });
+ }
+
+ public static class FooBar {
+ public static void main(String[] args) {
+ System.out.println(R.drawable.foo);
+ }
+ }
+
+ public static class R {
+ public static class drawable {
+ public static int foo;
+ public static int bar;
+ }
+ }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java b/src/test/testbase/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
index 2f9a852..92e0618 100644
--- a/src/test/testbase/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
+++ b/src/test/testbase/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.utils.StreamUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.Lists;
import com.google.common.collect.MoreCollectors;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.File;
@@ -46,6 +47,8 @@
import java.util.zip.ZipOutputStream;
import org.junit.Assert;
import org.junit.rules.TemporaryFolder;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Opcodes;
public class AndroidResourceTestingUtils {
private static final String RESOURCES_DESCRIPTOR =
@@ -307,6 +310,7 @@
private final List<Class<?>> classesToRemap = new ArrayList<>();
private int packageId = 0x7f;
private String packageName;
+ private boolean useStaticValueForNonFinalFields = false;
// Create the android resources from the passed in R classes
// All values will be generated based on the fields in the class.
@@ -344,6 +348,14 @@
return this;
}
+ // When set, use final ids for the AAPT resource generation, but remove the final bit in the
+ // ASM pass. This will emulate the R classes that AGP generates, where the static fields are
+ // set with a static value instead of through <clinit>.
+ AndroidTestResourceBuilder useStaticValueForNonFinalFields() {
+ this.useStaticValueForNonFinalFields = true;
+ return this;
+ }
+
AndroidTestResourceBuilder withManifest(String manifest) {
this.manifest = manifest;
return this;
@@ -485,7 +497,14 @@
String aaptPackageName =
packageName != null ? packageName : "thepackage" + packageId + ".foobar";
compileWithAapt2(
- resFolder, manifestPath, rClassOutputDir, output, temp, packageId, aaptPackageName);
+ resFolder,
+ manifestPath,
+ rClassOutputDir,
+ output,
+ temp,
+ packageId,
+ aaptPackageName,
+ !useStaticValueForNonFinalFields);
Path rClassJavaFile =
Files.walk(rClassOutputDir)
.filter(path -> path.endsWith("R.java"))
@@ -541,6 +560,19 @@
}
@Override
+ public FieldVisitor visitField(
+ int access,
+ String name,
+ String descriptor,
+ String signature,
+ Object value) {
+ if (useStaticValueForNonFinalFields) {
+ access &= ~Opcodes.ACC_FINAL;
+ }
+ return super.visitField(access, name, descriptor, signature, value);
+ }
+
+ @Override
public void visitInnerClass(
String name, String outerName, String innerName, int access) {
// Don't make the inner<>outer class connection
@@ -627,7 +659,8 @@
Path resourceZip,
TemporaryFolder temp,
int packageId,
- String packageName)
+ String packageName,
+ boolean nonFinalIds)
throws IOException {
Path compileOutput = temp.newFile("compiled.zip").toPath();
ProcessResult compileProcessResult =
@@ -635,8 +668,8 @@
"compile", "-o", compileOutput.toString(), "--dir", resFolder.toString());
failOnError(compileProcessResult);
- ProcessResult linkProcesResult =
- ToolHelper.runAapt2(
+ List<String> args =
+ Lists.newArrayList(
"link",
"-I",
ToolHelper.getAndroidJar(AndroidApiLevel.S).toString(),
@@ -644,7 +677,6 @@
resourceZip.toString(),
"--java",
rClassFolder.toString(),
- "--non-final-ids",
"--manifest",
manifest.toString(),
"--package-id",
@@ -654,6 +686,11 @@
packageName,
"--proto-format",
compileOutput.toString());
+ if (nonFinalIds) {
+ args.add("--non-final-ids");
+ }
+
+ ProcessResult linkProcesResult = ToolHelper.runAapt2(args.toArray(new String[0]));
failOnError(linkProcesResult);
}