blob: 3243171f36e3477f2efa755e0012febee4bf487a [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.ir.optimize.switches;
import static com.android.tools.r8.utils.BooleanUtils.intValue;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class ConvertRemovedStringSwitchTest extends TestBase {
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ConvertRemovedStringSwitchTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(ConvertRemovedStringSwitchTest.class)
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class, "A", "B", "C", "D", "E")
.assertSuccessWithOutputLines("A", "B", "C", "D", "E!");
}
private void inspect(CodeInspector inspector) {
ClassSubject classSubject = inspector.clazz(TestClass.class);
assertThat(classSubject, isPresent());
MethodSubject mainMethodSubject = classSubject.mainMethod();
assertThat(mainMethodSubject, isPresent());
// Verify that the keys were canonicalized.
Object2IntMap<String> stringCounts = countStrings(mainMethodSubject);
assertEquals(1 + intValue(parameters.isCfRuntime()), stringCounts.getInt("A"));
assertEquals(1 + intValue(parameters.isCfRuntime()), stringCounts.getInt("B"));
assertEquals(1 + intValue(parameters.isCfRuntime()), stringCounts.getInt("C"));
assertEquals(1 + intValue(parameters.isCfRuntime()), stringCounts.getInt("D"));
assertEquals(1, stringCounts.getInt("E"));
assertEquals(1, stringCounts.getInt("E!"));
// Verify that we can rebuild the StringSwitch instruction.
IRCode code = mainMethodSubject.buildIR();
assertTrue(code.streamInstructions().anyMatch(Instruction::isStringSwitch));
}
private static Object2IntMap<String> countStrings(MethodSubject methodSubject) {
Object2IntMap<String> result = new Object2IntOpenHashMap<>();
methodSubject
.streamInstructions()
.filter(instruction -> instruction.isConstString(JumboStringMode.ALLOW))
.map(InstructionSubject::getConstString)
.forEach(string -> result.put(string, result.getInt(string) + 1));
return result;
}
static class TestClass {
public static void main(String[] args) {
for (String arg : args) {
switch (arg) {
case "A":
System.out.println("A");
break;
case "B":
System.out.println("B");
break;
case "C":
System.out.println("C");
break;
case "D":
System.out.println("D");
break;
case "E":
// Intentionally "E!" to prevent canonicalization of this key.
System.out.println("E!");
break;
}
}
}
}
}