Support fields of type Enum
Bug:147860220
Change-Id: I196d43cf7e46e6c7898bd0ccd197d4617a0ebbf3
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/EnumUnboxer.java
index 8e46dd3..88166e5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/EnumUnboxer.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -15,6 +16,7 @@
import com.android.tools.r8.ir.analysis.type.ArrayTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeMethod;
@@ -226,6 +228,28 @@
return Reason.ELIGIBLE;
}
+ // A field put is valid only if the field is not on an enum, and the field type and the valuePut
+ // have identical enum type.
+ if (instruction.isFieldPut()) {
+ FieldInstruction fieldInstruction = instruction.asFieldInstruction();
+ DexEncodedField field = appView.appInfo().resolveField(fieldInstruction.getField());
+ if (field == null) {
+ return Reason.INVALID_FIELD_PUT;
+ }
+ DexProgramClass dexClass = appView.definitionForProgramType(field.field.holder);
+ if (dexClass == null) {
+ return Reason.INVALID_FIELD_PUT;
+ }
+ if (dexClass.isEnum()) {
+ return Reason.FIELD_PUT_ON_ENUM;
+ }
+ // The put value has to be of the field type.
+ if (field.field.type != enumClass.type) {
+ return Reason.TYPE_MISSMATCH_FIELD_PUT;
+ }
+ return Reason.ELIGIBLE;
+ }
+
if (instruction.isAssume()) {
Value outValue = instruction.outValue();
return validateEnumUsages(code, outValue.uniqueUsers(), outValue.uniquePhiUsers(), enumClass);
@@ -291,6 +315,9 @@
NAME_INVOKE,
UNSUPPORTED_LIBRARY_CALL,
MISSING_INFO_MAP,
+ INVALID_FIELD_PUT,
+ FIELD_PUT_ON_ENUM,
+ TYPE_MISSMATCH_FIELD_PUT,
OTHER_UNSUPPORTED_INSTRUCTION;
}
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 19c6edd..2defd19 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -150,6 +150,13 @@
return addKeepMainRule(mainClass.getTypeName());
}
+ public T addKeepMainRules(Class<?>[] mainClasses) {
+ for (Class<?> mainClass : mainClasses) {
+ this.addKeepMainRule(mainClass);
+ }
+ return self();
+ }
+
public T addKeepMainRule(String mainClass) {
return addKeepRules(
"-keep class " + mainClass + " { public static void main(java.lang.String[]); }");
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
index 57bbc5c..762380a 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingAnalysisTest.java
@@ -26,8 +26,8 @@
private static final Class<?>[] FAILURES = {
NullCheck.class,
Check.class,
- FieldPut.class,
- FieldPutObject.class,
+ InstanceFieldPutObject.class,
+ StaticFieldPutObject.class,
ToString.class,
EnumSetTest.class,
FailingPhi.class
@@ -81,8 +81,10 @@
assertTrue(inspector.clazz(NullCheck.class).uniqueMethodWithName("nullCheck").isPresent());
assertTrue(inspector.clazz(Check.class).uniqueMethodWithName("check").isPresent());
- assertEquals(1, inspector.clazz(FieldPut.class).allInstanceFields().size());
- assertEquals(1, inspector.clazz(FieldPutObject.class).allInstanceFields().size());
+ assertEquals(
+ 1, inspector.clazz(InstanceFieldPutObject.class).getDexClass().instanceFields().size());
+ assertEquals(
+ 1, inspector.clazz(StaticFieldPutObject.class).getDexClass().staticFields().size());
assertTrue(inspector.clazz(FailingPhi.class).uniqueMethodWithName("switchOn").isPresent());
}
@@ -131,29 +133,7 @@
}
}
- static class FieldPut {
-
- enum MyEnum {
- A,
- B,
- C
- }
-
- MyEnum e;
-
- public static void main(String[] args) {
- FieldPut fieldPut = new FieldPut();
- fieldPut.setA();
- System.out.println(fieldPut.e.ordinal());
- System.out.println(0);
- }
-
- void setA() {
- e = MyEnum.A;
- }
- }
-
- static class FieldPutObject {
+ static class InstanceFieldPutObject {
enum MyEnum {
A,
@@ -164,7 +144,7 @@
Object e;
public static void main(String[] args) {
- FieldPutObject fieldPut = new FieldPutObject();
+ InstanceFieldPutObject fieldPut = new InstanceFieldPutObject();
fieldPut.setA();
Object obj = new Object();
fieldPut.e = obj;
@@ -177,6 +157,29 @@
}
}
+ static class StaticFieldPutObject {
+
+ enum MyEnum {
+ A,
+ B,
+ C
+ }
+
+ static Object e;
+
+ public static void main(String[] args) {
+ setA();
+ Object obj = new Object();
+ e = obj;
+ System.out.println(e);
+ System.out.println(obj);
+ }
+
+ static void setA() {
+ e = MyEnum.A;
+ }
+ }
+
static class ToString {
enum MyEnum {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java
new file mode 100644
index 0000000..2841689
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingAnalysisTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2020, 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.enumunboxing;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FieldPutEnumUnboxingAnalysisTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private static final Class<?>[] INPUTS =
+ new Class<?>[] {InstanceFieldPut.class, StaticFieldPut.class};
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public FieldPutEnumUnboxingAnalysisTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(FieldPutEnumUnboxingAnalysisTest.class)
+ .addKeepMainRules(INPUTS)
+ .addKeepRules(KEEP_ENUM)
+ .addOptionsModification(this::enableEnumOptions)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .noMinification()
+ .compile()
+ .inspect(
+ i -> {
+ assertEquals(
+ 1, i.clazz(InstanceFieldPut.class).getDexClass().instanceFields().size());
+ assertEquals(
+ 1, i.clazz(StaticFieldPut.class).getDexClass().staticFields().size());
+ });
+
+ for (Class<?> input : INPUTS) {
+ R8TestRunResult run =
+ compile
+ .inspectDiagnosticMessages(
+ m -> assertEnumIsUnboxed(input.getDeclaredClasses()[0], input.getSimpleName(), m))
+ .run(parameters.getRuntime(), input)
+ .assertSuccess();
+ assertLines2By2Correct(run.getStdOut());
+ }
+ }
+
+ static class InstanceFieldPut {
+
+ enum MyEnum {
+ A,
+ B,
+ C
+ }
+
+ MyEnum e;
+
+ public static void main(String[] args) {
+ InstanceFieldPut fieldPut = new InstanceFieldPut();
+ fieldPut.setA();
+ System.out.println(fieldPut.e.ordinal());
+ System.out.println(0);
+ fieldPut.setB();
+ System.out.println(fieldPut.e.ordinal());
+ System.out.println(1);
+ }
+
+ void setA() {
+ e = MyEnum.A;
+ }
+
+ void setB() {
+ e = MyEnum.B;
+ }
+ }
+
+ static class StaticFieldPut {
+
+ enum MyEnum {
+ A,
+ B,
+ C
+ }
+
+ static MyEnum e;
+
+ public static void main(String[] args) {
+ setA();
+ System.out.println(StaticFieldPut.e.ordinal());
+ System.out.println(0);
+ setB();
+ System.out.println(StaticFieldPut.e.ordinal());
+ System.out.println(1);
+ }
+
+ @NeverInline
+ static void setA() {
+ StaticFieldPut.e = MyEnum.A;
+ }
+
+ @NeverInline
+ static void setB() {
+ StaticFieldPut.e = MyEnum.B;
+ }
+ }
+}