Enum unboxing: fix annotation with method parameters
Bug: 154950530
Change-Id: I68a044f507ccf7bb46f0884c341e48bc3c7e232f
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index 8f228b9..5e4eaed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -131,7 +131,7 @@
private void removeEnumsInAnnotations() {
for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (appView.appInfo().isSubtype(clazz.type, factory.annotationType)) {
+ if (clazz.isAnnotation()) {
removeEnumsInAnnotation(clazz);
}
}
@@ -142,13 +142,15 @@
// Each annotation value is represented by a virtual method.
for (DexEncodedMethod method : clazz.virtualMethods()) {
DexProto proto = method.method.proto;
- assert proto.parameters.isEmpty();
- DexType valueType = proto.returnType.toBaseType(appView.appInfo().dexItemFactory());
- if (valueType.isClassType()
- && enumToUnboxCandidates.containsKey(valueType)
- && appView.appInfo().isSubtype(valueType, appView.appInfo().dexItemFactory().enumType)) {
- enumUnboxer.reportFailure(valueType, Reason.ANNOTATION);
- enumToUnboxCandidates.remove(valueType);
+ // There can be references to enum unboxing candidates even if the parameter list is non
+ // empty. That is possible by injecting methods in the bytecode, but such methods are no
+ // different from other methods in the program, and can be rewritten by enum unboxing.
+ if (proto.parameters.isEmpty()) {
+ DexType valueType = proto.returnType.toBaseType(appView.appInfo().dexItemFactory());
+ if (enumToUnboxCandidates.containsKey(valueType)) {
+ enumUnboxer.reportFailure(valueType, Reason.ANNOTATION);
+ enumToUnboxCandidates.remove(valueType);
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
index 7cc0930..f9a5d80 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
@@ -5,9 +5,17 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import org.junit.Assume;
import org.junit.Test;
@@ -40,17 +48,16 @@
"The methods values and valueOf are required for reflection.",
enumKeepRules.toString().equals("none"));
testForR8(parameters.getBackend())
- .addInnerClasses(AnnotationEnumUnboxingTest.class)
+ .addProgramFiles(getProgramClasses())
+ .addProgramClassFileData(getProgramClassesData())
+ .noMinification()
.addKeepMainRule(Main.class)
.addKeepRules(enumKeepRules.getKeepRule())
- .addKeepRules(
- "-keep @interface"
- + " com.android.tools.r8.enumunboxing."
- + "AnnotationEnumUnboxingTest$ClassAnnotationDefault"
- + " { }",
- "-keepattributes *Annotation*")
+ .addKeepClassRules(ClassAnnotationDefault.class)
+ .addKeepRuntimeVisibleAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
+ .enableMergeAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
@@ -62,9 +69,31 @@
assertEnumIsBoxed(
MyEnumArrayDefault.class, MyEnumArrayDefault.class.getSimpleName(), m);
assertEnumIsBoxed(MyEnumArray.class, MyEnumArray.class.getSimpleName(), m);
+ assertEnumIsUnboxed(
+ MyEnumRetMethod2.class, MyEnumRetMethod2.class.getSimpleName(), m);
+ assertEnumIsUnboxed(
+ MyEnumParamMethod2.class, MyEnumParamMethod2.class.getSimpleName(), m);
})
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("print", "1", "1", "1", "1", "1", "0", "0", "0");
+ .assertSuccessWithOutputLines("print", "1", "1", "1", "1", "1", "0", "0", "0", "0");
+ }
+
+ private Collection<byte[]> getProgramClassesData() throws IOException {
+ return Collections.singletonList(transformAnnotationToBe());
+ }
+
+ private Collection<Path> getProgramClasses() throws IOException {
+ Collection<Path> inputs =
+ ToolHelper.getClassFilesForInnerClasses(
+ Collections.singletonList(AnnotationEnumUnboxingTest.class));
+ inputs.removeIf(p -> p.toString().contains("ClassAnnotationToBe.class"));
+ return inputs;
+ }
+
+ private byte[] transformAnnotationToBe() throws IOException {
+ return transformer(ClassAnnotationToBe.class)
+ .setAccessFlags(ClassAccessFlags::setAnnotation)
+ .transform();
}
@Retention(RetentionPolicy.RUNTIME)
@@ -78,6 +107,27 @@
MyEnumArray[] myEnumArray();
}
+ // This will be transformed into an annotation.
+ @NeverMerge
+ @NeverClassInline
+ interface ClassAnnotationToBe {
+ @NeverInline
+ @NeverPropagateValue
+ default MyEnumRetMethod2 enumMethod(MyEnumParamMethod2 param) {
+ return param == MyEnumParamMethod2.A ? MyEnumRetMethod2.A : MyEnumRetMethod2.B;
+ }
+ }
+
+ enum MyEnumParamMethod2 {
+ A,
+ B
+ }
+
+ enum MyEnumRetMethod2 {
+ A,
+ B
+ }
+
enum MyEnumDefault {
A,
B
@@ -111,6 +161,8 @@
}
}
+ static class ClassAnnotationToBeSub implements ClassAnnotationToBe {}
+
static class Main {
public static void main(String[] args) {
new ClassDefault().print();
@@ -124,6 +176,8 @@
System.out.println(annotation.myEnumArray()[0].ordinal());
System.out.println(annotation.myEnumArrayDefault()[0].ordinal());
System.out.println(annotation.myEnumDefault().ordinal());
+ // We need two classes toa void merging.
+ System.out.println(new ClassAnnotationToBeSub().enumMethod(MyEnumParamMethod2.A).ordinal());
}
}
}