blob: b32e476188c514e12c77e6477ae08be5172cf0b0 [file] [log] [blame]
// Copyright (c) 2021, 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.switches;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class MaxIntSwitchTest extends TestBase {
private final TestParameters parameters;
private final CompilationMode mode;
@Parameterized.Parameters(name = "{0}, mode = {1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(), CompilationMode.values());
}
// See b/177790310 for details.
public MaxIntSwitchTest(TestParameters parameters, CompilationMode mode) {
this.parameters = parameters;
this.mode = mode;
}
private void checkSwitch(InstructionSubject instruction, boolean hasMaxIntKey) {
assertEquals(hasMaxIntKey, instruction.asSwitch().getKeys().contains(0x7fffffff));
}
private void checkSwitch(MethodSubject method, boolean hasMaxIntKey) {
assertTrue(method.streamInstructions().anyMatch(InstructionSubject::isSwitch));
method
.streamInstructions()
.filter(InstructionSubject::isSwitch)
.forEach(instruction -> checkSwitch(instruction, hasMaxIntKey));
}
private void checkStringSwitch(MethodSubject method, boolean hasMaxIntKey) {
// String switch will have two switches.
List<InstructionSubject> switchInstructions =
method
.streamInstructions()
.filter(InstructionSubject::isSwitch)
.collect(Collectors.toList());
assertEquals(2, switchInstructions.size());
checkSwitch(switchInstructions.get(0), hasMaxIntKey);
}
public void checkSwitchKeys(CodeInspector inspector) {
checkSwitch(
inspector.clazz(TestClass.class).uniqueMethodWithName("f"),
parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K));
checkSwitch(
inspector.clazz(TestClass.class).uniqueMethodWithName("g"),
parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K));
// Debug mode will not rewrite switch statements except when the MAX_INT key is present.
assertEquals(
inspector
.clazz(TestClass.class)
.uniqueMethodWithName("h")
.streamInstructions()
.filter(InstructionSubject::isSwitch)
.count(),
BooleanUtils.intValue(
mode == CompilationMode.DEBUG
&& parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K)));
checkStringSwitch(
inspector.clazz(TestClass.class).uniqueMethodWithName("s"),
parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K));
}
@Test
public void testD8() throws Exception {
testForD8(parameters.getBackend())
.addInnerClasses(this.getClass())
.setMinApi(parameters.getApiLevel())
.setMode(mode)
.run(parameters.getRuntime(), TestClass.class)
.inspect(this::checkSwitchKeys)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(this.getClass())
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
.setMinApi(parameters.getApiLevel())
.setMode(mode)
.run(parameters.getRuntime(), TestClass.class)
.inspect(this::checkSwitchKeys)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
static final String EXPECTED_OUTPUT =
StringUtils.lines(
Integer.toString(0x7ffffffc),
Integer.toString(0x7ffffffd),
Integer.toString(0x7ffffffe),
Integer.toString(0x7fffffff),
"good",
"show",
"!",
"?");
static class TestClass {
@NeverInline
@NeverPropagateValue
public static void f(int i) {
switch (i) {
case 0x7ffffffd:
System.out.println("a");
break;
case 0x7ffffffe:
System.out.println("b");
break;
case 0x7fffffff:
System.out.println("good");
break;
default:
throw new AssertionError();
}
}
@NeverInline
@NeverPropagateValue
public static void g(int i) {
switch (i) {
case 0x7ffffffc:
System.out.println("a");
break;
case 0x7ffffffd:
System.out.println("b");
break;
case 0x7ffffffe:
case 0x7fffffff:
System.out.println("show");
break;
default:
throw new AssertionError();
}
}
@NeverInline
@NeverPropagateValue
public static void h(int i) {
switch (i) {
case 0x7fffffff:
System.out.println("!");
break;
default:
throw new AssertionError();
}
}
@NeverInline
@NeverPropagateValue
public static void s(String s) {
switch (s) {
case "DAEFIDU":
System.out.println("a");
break;
case "DAEFIDV":
System.out.println("b");
break;
case "DAEFIDW":
System.out.println("c");
break;
case "DAEFIDX":
System.out.println("?");
break;
default:
throw new AssertionError();
}
}
public static void main(String[] args) {
System.out.println("DAEFIDU".hashCode());
System.out.println("DAEFIDV".hashCode());
System.out.println("DAEFIDW".hashCode());
System.out.println("DAEFIDX".hashCode());
f(0x7fffffff);
g(0x7fffffff);
h(0x7fffffff);
s("DAEFIDX");
}
}
}