| // 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 com.android.tools.r8.NeverClassInline; |
| 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 java.util.List; |
| 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 InstanceFieldsEnumUnboxingTest extends EnumUnboxingTestBase { |
| |
| private static final Class<?>[] FAILURES = { |
| FailureIntField.class, |
| FailurePrivateIntField.class, |
| FailureBoxedInnerEnumField.class, |
| FailureUnboxedEnumField.class, |
| FailureTooManyUsedFields.class |
| }; |
| |
| private static final Class<?>[] SUCCESSES = { |
| SuccessUnusedField.class, |
| SuccessIntField.class, |
| SuccessDoubleField.class, |
| SuccessIntFieldOrdinal.class, |
| SuccessIntFieldInitializerInit.class, |
| SuccessStringField.class, |
| SuccessMultiConstructorIntField.class, |
| }; |
| |
| private final TestParameters parameters; |
| private final boolean enumValueOptimization; |
| private final EnumKeepRules enumKeepRules; |
| |
| @Parameters(name = "{0} valueOpt: {1} keep: {2}") |
| public static List<Object[]> data() { |
| return enumUnboxingTestParameters(); |
| } |
| |
| public InstanceFieldsEnumUnboxingTest( |
| TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) { |
| this.parameters = parameters; |
| this.enumValueOptimization = enumValueOptimization; |
| this.enumKeepRules = enumKeepRules; |
| } |
| |
| @Test |
| public void testEnumUnboxing() throws Exception { |
| R8TestCompileResult compile = |
| testForR8(parameters.getBackend()) |
| .addInnerClasses(InstanceFieldsEnumUnboxingTest.class) |
| .addKeepMainRules(SUCCESSES) |
| .addKeepMainRules(FAILURES) |
| .noMinification() |
| .enableInliningAnnotations() |
| .enableNeverClassInliningAnnotations() |
| .addKeepRules(enumKeepRules.getKeepRules()) |
| .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization)) |
| .allowDiagnosticInfoMessages() |
| .setMinApi(parameters.getApiLevel()) |
| .compile(); |
| for (Class<?> failure : FAILURES) { |
| testClass(compile, failure, true); |
| } |
| for (Class<?> success : SUCCESSES) { |
| testClass(compile, success, false); |
| } |
| } |
| |
| private void testClass(R8TestCompileResult compile, Class<?> testClass, boolean failure) |
| throws Exception { |
| R8TestRunResult run = |
| compile |
| .inspectDiagnosticMessages( |
| m -> { |
| for (Class<?> declaredClass : testClass.getDeclaredClasses()) { |
| if (declaredClass.isEnum() |
| && !declaredClass.getSimpleName().equals("InnerEnum")) { |
| if (failure) { |
| assertEnumIsBoxed(declaredClass, testClass.getSimpleName(), m); |
| } else { |
| assertEnumIsUnboxed(declaredClass, testClass.getSimpleName(), m); |
| } |
| } |
| } |
| }) |
| .run(parameters.getRuntime(), testClass) |
| .assertSuccess(); |
| assertLines2By2Correct(run.getStdOut()); |
| } |
| |
| static class SuccessUnusedField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().ordinal()); |
| System.out.println(0); |
| System.out.println(getEnumB().ordinal()); |
| System.out.println(1); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(10), |
| B(20); |
| |
| int field; |
| |
| EnumField(int i) { |
| this.field = i; |
| } |
| } |
| } |
| |
| static class SuccessIntField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field); |
| System.out.println(10); |
| System.out.println(getEnumB().field); |
| System.out.println(20); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(10), |
| B(20); |
| |
| int field; |
| |
| EnumField(int i) { |
| this.field = i; |
| } |
| } |
| } |
| |
| static class FailurePrivateIntField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field); |
| System.out.println(10); |
| System.out.println(getEnumB().field); |
| System.out.println(20); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(10), |
| B(20); |
| |
| private int field; |
| |
| EnumField(int i) { |
| this.field = i; |
| } |
| } |
| } |
| |
| static class SuccessDoubleField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field); |
| System.out.println(10.0); |
| System.out.println(getEnumB().field); |
| System.out.println(20.0); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(10.0), |
| B(20.0); |
| |
| double field; |
| |
| EnumField(double d) { |
| this.field = d; |
| } |
| } |
| } |
| |
| static class SuccessIntFieldInitializerInit { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field); |
| System.out.println(10); |
| System.out.println(getEnumB().field); |
| System.out.println(10); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A, |
| B, |
| C; |
| |
| int field; |
| |
| EnumField() { |
| this.field = 10; |
| } |
| } |
| } |
| |
| // This class test an optimization where the ordinal is re-used instead of the int field. |
| static class SuccessIntFieldOrdinal { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field); |
| System.out.println(0); |
| System.out.println(getEnumB().field); |
| System.out.println(1); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(0), |
| B(1), |
| C(2); |
| |
| int field; |
| |
| EnumField(int i) { |
| this.field = i; |
| } |
| } |
| } |
| |
| static class FailureIntField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field); |
| System.out.println(30); |
| System.out.println(getEnumB().field); |
| System.out.println(60); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(getRandom(10)), |
| B(getRandom(20)), |
| C(getRandom(30)); |
| |
| @NeverInline |
| static int getRandom(int i) { |
| return i * (System.currentTimeMillis() > 0 ? 3 : -3); |
| } |
| |
| int field; |
| |
| EnumField(int i) { |
| this.field = i; |
| } |
| } |
| } |
| |
| static class FailureTooManyUsedFields { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field0); |
| System.out.println(0); |
| System.out.println(getEnumA().field1); |
| System.out.println(9); |
| System.out.println(getEnumA().field2); |
| System.out.println(8); |
| System.out.println(getEnumA().field3); |
| System.out.println(7); |
| System.out.println(getEnumA().field4); |
| System.out.println(6); |
| System.out.println(getEnumA().field5); |
| System.out.println(5); |
| System.out.println(getEnumA().field6); |
| System.out.println(4); |
| System.out.println(getEnumA().field7); |
| System.out.println(3); |
| System.out.println(getEnumA().field8); |
| System.out.println(2); |
| System.out.println(getEnumA().field9); |
| System.out.println(1); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(1, 2, 3, 4, 5, 6, 7, 8, 9, 0), |
| B(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); |
| |
| int field0; |
| int field1; |
| int field2; |
| int field3; |
| int field4; |
| int field5; |
| int field6; |
| int field7; |
| int field8; |
| int field9; |
| |
| EnumField(int i9, int i8, int i7, int i6, int i5, int i4, int i3, int i2, int i1, int i0) { |
| this.field0 = i0; |
| this.field1 = i1; |
| this.field2 = i2; |
| this.field3 = i3; |
| this.field4 = i4; |
| this.field5 = i5; |
| this.field6 = i6; |
| this.field7 = i7; |
| this.field8 = i8; |
| this.field9 = i9; |
| } |
| } |
| } |
| |
| static class SuccessMultiConstructorIntField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field0); |
| System.out.println(10); |
| System.out.println(getEnumA().field1); |
| System.out.println(-1); |
| System.out.println(getEnumB().field0); |
| System.out.println(20); |
| System.out.println(getEnumB().field1); |
| System.out.println(30); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(10), |
| B(20, 30); |
| |
| int field0; |
| int field1; |
| |
| EnumField(int i0) { |
| this(i0, -1); |
| } |
| |
| EnumField(int i0, int i1) { |
| this.field0 = i0; |
| this.field1 = i1; |
| } |
| } |
| } |
| |
| static class SuccessStringField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field); |
| System.out.println("AA"); |
| System.out.println(getEnumB().field); |
| System.out.println("BB"); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A("AA"), |
| B("BB"), |
| C("CC"); |
| |
| String field; |
| |
| EnumField(String s) { |
| this.field = s; |
| } |
| } |
| } |
| |
| static class FailureBoxedInnerEnumField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field); |
| System.out.println("X"); |
| System.out.println(getEnumB().field); |
| System.out.println("Y"); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum InnerEnum { |
| X, |
| Y, |
| Z; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(InnerEnum.X), |
| B(InnerEnum.Y), |
| C(InnerEnum.Z); |
| |
| InnerEnum field; |
| |
| EnumField(InnerEnum s) { |
| this.field = s; |
| } |
| } |
| } |
| |
| static class FailureUnboxedEnumField { |
| |
| public static void main(String[] args) { |
| System.out.println(getEnumA().field.ordinal()); |
| System.out.println(0); |
| System.out.println(getEnumB().field.ordinal()); |
| System.out.println(1); |
| } |
| |
| @NeverInline |
| static EnumField getEnumA() { |
| return System.currentTimeMillis() > 0 ? EnumField.A : EnumField.B; |
| } |
| |
| @NeverInline |
| static EnumField getEnumB() { |
| return System.currentTimeMillis() > 0 ? EnumField.B : EnumField.A; |
| } |
| |
| @NeverClassInline |
| enum InnerEnum { |
| X, |
| Y, |
| Z; |
| } |
| |
| @NeverClassInline |
| enum EnumField { |
| A(InnerEnum.X), |
| B(InnerEnum.Y), |
| C(InnerEnum.Z); |
| |
| InnerEnum field; |
| |
| EnumField(InnerEnum s) { |
| this.field = s; |
| } |
| } |
| } |
| } |