blob: 2cb5556238b424805205b364bf69e07b666b1dc4 [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.desugar.backports;
import static org.hamcrest.core.StringContains.containsString;
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class ApiLevelBackportsTest extends TestBase {
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
// NOTE: Most of the 'run' invocations below work only because the static configured APIs do not
// give rise to DEX file versions larger than what can be accepted by VM 9.0.0.
return getTestParameters().withDexRuntimesStartingFromIncluding(Version.V9_0_0).build();
}
public ApiLevelBackportsTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void backportSucceedsOnSupportedApiLevel() throws Exception {
testForD8()
.addProgramClassFileData(transformTestMathMultiplyExactLongInt())
.setMinApi(AndroidApiLevel.B)
.run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
.assertSuccessWithOutputLines("4");
}
@Test
public void backportOfNonPresentMethodOnLatest() throws Exception {
testForD8()
.addProgramClassFileData(transformTestMathMultiplyExactLongInt())
.setMinApi(AndroidApiLevel.LATEST)
.compile()
.assertNoMessages()
.run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
.assertSuccessWithOutputLines("4");
}
@Test
public void backportOfPresentMethodOnLatest() throws Exception {
D8TestRunResult result =
testForD8()
.addProgramClassFileData(transformTestListOf())
.setMinApi(AndroidApiLevel.LATEST)
.compile()
.assertNoMessages()
.run(parameters.getRuntime(), TestListOf.class);
if (runtimeHasListOf()) {
result.assertSuccessWithOutputLines("0");
} else {
result.assertFailureWithErrorThatMatches(
containsString("java.lang.NoSuchMethodError: No static method of()Ljava/util/List;"));
}
}
@Test
public void warningForFutureNonPlatformBuild() throws Exception {
testForD8()
.addProgramClassFileData(transformTestMathMultiplyExactLongInt())
.setMinApi(AndroidApiLevel.LATEST.getLevel() + 1)
.compile()
.assertOnlyWarnings()
.assertWarningMessageThatMatches(containsString("is not supported by this compiler"))
.run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
.assertSuccessWithOutputLines("4");
}
@Test
public void noWarningForPlatformBuild() throws Exception {
testForD8()
.addProgramClassFileData(transformTestMathMultiplyExactLongInt())
.setMinApi(AndroidApiLevel.ANDROID_PLATFORM)
.run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
.applyIf(
parameters.getDexRuntimeVersion().isOlderThan(Version.V13_MASTER),
b ->
b.assertFailureWithErrorThatMatches(
containsString(
"java.lang.NoSuchMethodError: No static method"
+ " parseInt(Ljava/lang/CharSequence;III)I")),
b -> b.assertSuccessWithOutputLines("4"));
}
// Test class for using: List List.of()
// Introduced in Android R.
boolean runtimeHasListOf() {
return parameters
.getRuntime()
.asDex()
.getMinApiLevel()
.isGreaterThanOrEqualTo(AndroidApiLevel.R);
}
byte[] transformTestListOf() throws Exception {
return transformer(TestListOf.class)
.transformMethodInsnInMethod(
"main",
(opcode, owner, name, descriptor, isInterface, visitor) -> {
if (name.equals("List_of")) {
visitor.visitMethodInsn(opcode, "java/util/List", "of", descriptor, isInterface);
} else {
visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
}
static class TestListOf {
public static List List_of() {
throw null;
}
public static void main(String[] args) {
System.out.println(List_of().size());
}
}
// Test class for the method: long Math.multiplyExact(long, int)
// Not present on any currently known Android platforms.
boolean runtimeHasMathMultiplyExactLongInt() {
// NOTE: This may change with a future release.
return false;
}
byte[] transformTestMathMultiplyExactLongInt() throws Exception {
return transformer(TestMathMultiplyExactLongInt.class)
.transformMethodInsnInMethod(
"main",
(opcode, owner, name, descriptor, isInterface, visitor) -> {
if (name.equals("Integer_parseInt")) {
visitor.visitMethodInsn(
opcode, "java/lang/Integer", "parseInt", descriptor, isInterface);
} else {
visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
}
static class TestMathMultiplyExactLongInt {
public static int Integer_parseInt(CharSequence s, int beginIndex, int endIndex, int radix) {
throw null;
}
public static void main(String[] args) {
System.out.println(Integer_parseInt("4", 0, 1, 10));
}
}
}