blob: fe575ed504752caea0332b9e013f554de338558d [file] [log] [blame]
// 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.rewrite.enums;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase.EnumKeepRules;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
// This is testing various edge cases of the Enum.valueOf(Class, String) optimization, such as
// passing an enum subclass constant as the first argument to Enum.valueOf. The implementation of
// Enum.valueOf reflects on the user program and keep rules are therefore needed for the program
// to produce the expected result after shrinking.
@RunWith(Parameterized.class)
public class EnumValueOfOptimizationTest extends TestBase {
@Parameter(0)
public boolean enableNoVerticalClassMergingAnnotations;
@Parameter(1)
public EnumKeepRules enumKeepRules;
@Parameter(2)
public TestParameters parameters;
@Parameters(name = "{2}, annotations: {0}, keep: {1}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
EnumKeepRules.values(),
getTestParameters().withAllRuntimesAndApiLevels().build());
}
@Test
public void testValueOf() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(EnumValueOfOptimizationTest.class)
.addKeepMainRule(Main.class)
.addKeepRules(enumKeepRules.getKeepRules())
.applyIf(
enableNoVerticalClassMergingAnnotations,
R8TestBuilder::enableNoVerticalClassMergingAnnotations,
TestShrinkerBuilder::addNoVerticalClassMergingAnnotations)
.setMinApi(parameters)
.compile()
.run(parameters.getRuntime(), Main.class)
.applyIf(
parameters.isCfRuntime()
|| enableNoVerticalClassMergingAnnotations
|| enumKeepRules.isStudio(),
runResult ->
runResult.assertSuccessWithOutputLines(
"npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "iae4 OK"),
parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
runResult ->
runResult.assertSuccessWithOutputLines(
"npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "npe OK"),
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V10_0_0),
runResult ->
runResult.assertSuccessWithOutputLines(
"npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "nsme->re OK"),
runResult ->
runResult.assertSuccessWithOutputLines(
"npe OK", "iae1 OK", "iae2 OK", "iae3 OK", "nsme->ae OK"));
}
enum MyEnum {
A,
B
}
@NoVerticalClassMerging
enum ComplexEnum {
A {
@Override
public String toString() {
return "a0";
}
},
B
}
@SuppressWarnings({"unchecked", "ConstantConditions"})
static class Main {
public static void main(String[] args) {
myEnumTest();
complexEnumTest();
}
private static void complexEnumTest() {
Enum<?> e = null;
try {
e = subtypeError();
} catch (AssertionError ae) {
if (ae.getCause() instanceof NoSuchMethodException) {
System.out.println("nsme->ae OK");
} else {
System.out.println(ae.getClass().getName());
System.out.println(ae.getCause().getClass().getName());
}
} catch (IllegalArgumentException iae) {
System.out.println("iae4 OK");
} catch (NullPointerException npe) {
System.out.println("npe OK");
} catch (RuntimeException re) {
if (re.getClass() == RuntimeException.class
&& re.getCause() instanceof NoSuchMethodException) {
System.out.println("nsme->re OK");
} else {
System.out.println(re.getClass().getName());
System.out.println(re.getCause().getClass().getName());
}
}
if (e != null) {
throw new Error("enum set");
}
}
private static Enum<?> subtypeError() {
return Enum.valueOf((Class) ComplexEnum.A.getClass(), "A");
}
private static void myEnumTest() {
Enum<?> e = null;
try {
e = nullClassError();
System.out.println("npe KO");
} catch (NullPointerException ignored) {
System.out.println("npe OK");
}
if (e != null) {
throw new Error("enum set");
}
try {
e = invalidNameError();
System.out.println("iae1 KO");
} catch (IllegalArgumentException iae) {
System.out.println("iae1 OK");
}
if (e != null) {
throw new Error("enum set");
}
try {
e = enumClassError();
System.out.println("iae2 KO");
} catch (IllegalArgumentException iae) {
System.out.println("iae2 OK");
}
if (e != null) {
throw new Error("enum set");
}
try {
e = voidError();
System.out.println("iae3 KO");
} catch (IllegalArgumentException iae) {
System.out.println("iae3 OK");
}
if (e != null) {
throw new Error("enum set");
}
}
private static Enum<?> voidError() {
return Enum.valueOf((Class) Void.class, "TYPE");
}
private static Enum<?> enumClassError() {
return Enum.valueOf(Enum.class, "smth");
}
private static Enum<?> invalidNameError() {
return Enum.valueOf(MyEnum.class, "curly");
}
private static Enum<?> nullClassError() {
return Enum.valueOf((Class<MyEnum>) null, "a string");
}
}
}