blob: 5ad2a81d0ed254820abd49ae9bd5c0ba718b389d [file] [log] [blame]
// Copyright (c) 2022, 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.optimize.argumentpropagation;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThrows;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
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 an attempt on a regression test for b/250634405. What happens in the input program is
* that we determine a phi value to be always null in Phi.getDynamicUpperBoundType. That information
* has not been propagated to the receiver during optimizing of the IR. Therefor the check at {@link
* ArgumentPropagatorCodeScanner.scan} for receiver always being null returns false.
*
* <p>Getting the exact IR to match was difficult so this test short circuit this by disabling IR
* processing of a simple method (by specifying pass-through) and disabling the check in {@link
* ArgumentPropagatorCodeScanner.scan}.
*/
@RunWith(Parameterized.class)
public class PolymorphicMethodWithNullReceiverBoundTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withCfRuntimes().build();
}
@Test
public void testR8WithTestAssertionsEnabled() {
assertThrows(
CompilationFailedException.class,
() -> {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.enableNoHorizontalClassMergingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addOptionsModification(
options -> {
options.testing.cfByteCodePassThrough =
method -> method.getName().startsWith("main");
options.testing.checkReceiverAlwaysNullInCallSiteOptimization = false;
})
.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics.assertErrorMessageThatMatches(containsString("b/250634405"));
});
});
}
@Test
public void testR8WithoutTestAssertions() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.enableNoHorizontalClassMergingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addOptionsModification(
options -> {
options.testing.cfByteCodePassThrough = method -> method.getName().startsWith("main");
options.testing.checkReceiverAlwaysNullInCallSiteOptimization = false;
options.testing.enableTestAssertions = false;
})
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatThrows(NullPointerException.class);
}
static class Main {
public static void main(String[] args) {
I i = System.currentTimeMillis() > 0 ? new A() : new B();
i = null;
i.m();
}
}
public interface I {
ReturnType m();
}
@NoHorizontalClassMerging
@NoVerticalClassMerging
static class A implements I {
@Override
public ReturnType m() {
System.out.println("A.m()");
return new ReturnType();
}
}
static class B implements I {
@Override
public ReturnType m() {
System.out.println("B.m()");
return new ReturnType();
}
}
@NoHorizontalClassMerging
static class ReturnType {}
}