blob: 5a23263da8227cf7f3641f1d60f5367af12aba17 [file] [log] [blame]
// Copyright (c) 2023, 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.boxedprimitives;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
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;
@RunWith(Parameterized.class)
public class BoxedPrimitiveFromGenericUnboxingTest extends TestBase {
@Parameter(0)
public boolean enableBridgeHoistingToSharedSyntheticSuperclass;
@Parameter(1)
public TestParameters parameters;
@Parameters(name = "{1}, opt: {0}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
@Test
public void test() throws Exception {
boolean optimize =
enableBridgeHoistingToSharedSyntheticSuperclass
&& parameters.canHaveNonReboundConstructorInvoke();
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addOptionsModification(
options ->
options.testing.enableBridgeHoistingToSharedSyntheticSuperclass =
enableBridgeHoistingToSharedSyntheticSuperclass)
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters)
.compile()
.inspect(
inspector -> {
// Function should be removed as a result of bridge hoisting + inlining when adding a
// shared superclass to Increment and Decrement, and another shared superclass to
// StdoutPrinter and StderrPrinter.
ClassSubject functionClassSubject = inspector.clazz(Function.class);
assertThat(functionClassSubject, isAbsentIf(optimize));
// Check that the cast to java.lang.Integer in Increment.apply has been removed as a
// result of devirtualization.
ClassSubject incrementClassSubject = inspector.clazz(Increment.class);
assertThat(incrementClassSubject, isPresent());
MethodSubject incrementApplyMethodSubject =
incrementClassSubject.uniqueMethodWithOriginalName("apply");
assertThat(incrementApplyMethodSubject, isPresent());
assertEquals(
optimize,
incrementApplyMethodSubject
.streamInstructions()
.noneMatch(
instruction -> instruction.isCheckCast(Integer.class.getTypeName())));
// Check that the cast to java.lang.String in StdoutPrinter.apply has been removed as
// result of devirtualization (in fact the `Void apply(String)` method has been
// optimized to `void apply()` as a result of constant propagation).
ClassSubject stdoutPrinterClassSubject = inspector.clazz(StdoutPrinter.class);
assertThat(stdoutPrinterClassSubject, isPresent());
MethodSubject stdoutPrinterApplyMethodSubject =
stdoutPrinterClassSubject.uniqueMethodWithOriginalName("apply");
assertThat(stdoutPrinterApplyMethodSubject, isPresent());
assertEquals(
optimize,
stdoutPrinterApplyMethodSubject.getProgramMethod().getReturnType().isVoidType());
assertEquals(
optimize ? 0 : 1, stdoutPrinterApplyMethodSubject.getParameters().size());
assertEquals(
optimize,
stdoutPrinterApplyMethodSubject
.streamInstructions()
.noneMatch(
instruction -> instruction.isCheckCast(String.class.getTypeName())));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("42", "42", "42");
}
static class Main {
public static void main(String[] args) {
Function<Integer, Integer> inc =
System.currentTimeMillis() > 0 ? new Increment() : new Decrement();
Function<Integer, Integer> dec =
System.currentTimeMillis() > 0 ? new Decrement() : new Increment();
Function<String, Void> printer =
System.currentTimeMillis() > 0 ? new StdoutPrinter() : new StderrPrinter();
System.out.println(inc.apply(41));
System.out.println(dec.apply(43));
printer.apply("42");
}
}
interface Function<S, T> {
T apply(S s);
}
@NoHorizontalClassMerging
static class Increment implements Function<Integer, Integer> {
@Override
public Integer apply(Integer i) {
return i + 1;
}
}
@NoHorizontalClassMerging
static class Decrement implements Function<Integer, Integer> {
@Override
public Integer apply(Integer i) {
return i - 1;
}
}
@NoHorizontalClassMerging
static class StdoutPrinter implements Function<String, Void> {
@Override
public Void apply(String obj) {
System.out.println(obj);
return null;
}
}
@NoHorizontalClassMerging
static class StderrPrinter implements Function<String, Void> {
@Override
public Void apply(String obj) {
System.err.println(obj);
return null;
}
}
}