Trace the values() method on emuns when used as default values in annotations
When an annotation have a field of an enum type with a default value then Java VM
will use the values() method on that enum class.
Bug: 137392797
Bug: 138156533
Change-Id: Ie6f26326f6315380ccac7f664d78ef0a174ee49c
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 67056fe..35cd3d1 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1692,7 +1692,7 @@
DexEncodedMethod valuesMethod = clazz.lookupMethod(generatedEnumValuesMethod(clazz));
if (valuesMethod != null) {
// TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
- // marking of not renaming it in the root set.
+ // marking for not renaming it is in the root set.
enqueueRootItem(valuesMethod, reason);
rootSet.shouldNotBeMinified(valuesMethod.toReference());
}
@@ -2625,6 +2625,15 @@
return false;
}
markStaticFieldAsLive(field, KeepReason.referencedInAnnotation(annotationHolder));
+ // When an annotation has a field of an enum type with a default value then Java VM
+ // will use the values() method on that enum class.
+ if (options.isGeneratingClassFiles()
+ && annotationHolder == dexItemFactory.annotationDefault) {
+ DexClass clazz = appView.definitionFor(field.type);
+ if (clazz != null && clazz.isProgramClass() && clazz.accessFlags.isEnum()) {
+ markEnumValuesAsReachable(clazz, KeepReason.referencedInAnnotation(annotationHolder));
+ }
+ }
}
} else {
target = holder.lookupInstanceField(field);
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index 47fae5a..8585a9c 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -10,10 +10,10 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -29,14 +29,17 @@
public class B137392797 extends TestBase implements Opcodes {
private final TestParameters parameters;
+ private final boolean defaultEnumValueInAnnotation;
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ @Parameterized.Parameters(name = "Backend: {0}, default value in annotation: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevels().build(), BooleanUtils.values());
}
- public B137392797(TestParameters parameters) {
+ public B137392797(TestParameters parameters, boolean defaultEnumValueInAnnotation) {
this.parameters = parameters;
+ this.defaultEnumValueInAnnotation = defaultEnumValueInAnnotation;
}
private void checkEnumUses(CodeInspector inspector) {
@@ -45,24 +48,26 @@
// Only 2 of the 5 enum values are actually used:
// * REQUIRED: annotation for Test.field1
// * OPTIONAL: default value of WireField.label
- // One more: values[]
- assertEquals(3, classSubject.allFields().size());
+ // When generating class file the field values[] is also present as values() is kept.
+ assertEquals(
+ parameters.isCfRuntime() && defaultEnumValueInAnnotation ? 3 : 2,
+ classSubject.allFields().size());
+ // Methods <clinit>, <init> always present. values() present if generating class file.
+ assertEquals(
+ parameters.isCfRuntime() && defaultEnumValueInAnnotation ? 3 : 2,
+ classSubject.allMethods().size());
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addProgramClassFileData(classWireField(), classWireFieldLabel(), classTest())
+ .addProgramClassFileData(
+ classWireField(defaultEnumValueInAnnotation),
+ classWireFieldLabel(),
+ classTest(defaultEnumValueInAnnotation))
.addProgramClasses(TestClass.class)
.addKeepClassAndMembersRules(
- "com.squareup.wire.WireField",
- "com.squareup.demo.myapplication.Test")
- // TODO(b/138156533): Need to trace enum values() if an instance is used as default value
- // for an annotation field.
- .addKeepRules(StringUtils.lines(
- "-keepclassmembers class com.squareup.wire.WireField$Label {",
- " public static *** values();",
- "}"))
+ "com.squareup.wire.WireField", "com.squareup.demo.myapplication.Test")
.addKeepMainRule(TestClass.class)
.addKeepAttributes("*Annotation*")
.setMinApi(parameters.getRuntime())
@@ -133,7 +138,7 @@
)
*/
- public static byte[] classWireField() throws Exception {
+ public static byte[] classWireField(boolean defaultEnumValueInAnnotation) throws Exception {
ClassWriter classWriter = new ClassWriter(0);
MethodVisitor methodVisitor;
@@ -220,9 +225,11 @@
null,
null);
{
- annotationVisitor0 = methodVisitor.visitAnnotationDefault();
- annotationVisitor0.visitEnum(null, "Lcom/squareup/wire/WireField$Label;", "OPTIONAL");
- annotationVisitor0.visitEnd();
+ if (defaultEnumValueInAnnotation) {
+ annotationVisitor0 = methodVisitor.visitAnnotationDefault();
+ annotationVisitor0.visitEnum(null, "Lcom/squareup/wire/WireField$Label;", "OPTIONAL");
+ annotationVisitor0.visitEnd();
+ }
}
methodVisitor.visitEnd();
}
@@ -637,7 +644,7 @@
return classWriter.toByteArray();
}
- public static byte[] classTest() throws Exception {
+ public static byte[] classTest(boolean defaultEnumValueInAnnotation) throws Exception {
ClassWriter classWriter = new ClassWriter(0);
FieldVisitor fieldVisitor;
@@ -691,6 +698,9 @@
annotationVisitor0 = fieldVisitor.visitAnnotation("Lcom/squareup/wire/WireField;", true);
annotationVisitor0.visit("tag", new Integer(1));
annotationVisitor0.visit("adapter", "com.squareup.wire.ProtoAdapter#STRING");
+ if (!defaultEnumValueInAnnotation) {
+ annotationVisitor0.visitEnum("label", "Lcom/squareup/wire/WireField$Label;", "OPTIONAL");
+ }
annotationVisitor0.visitEnd();
}
{