blob: cb02c8234be155a0b8e42ecc681af4f632e2838b [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.desugar.constantdynamic;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DesugarTestConfiguration;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import java.lang.invoke.MethodHandles;
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 ConstantDynamicInDefaultInterfaceMethodTest extends TestBase {
@Parameter() public TestParameters parameters;
@Parameters(name = "{0}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
}
private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true");
private static final Class<?> MAIN_CLASS = A.class;
@Test
public void testReference() throws Exception {
assumeTrue(parameters.isCfRuntime());
assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
testForJvm()
.addProgramClasses(MAIN_CLASS)
.addProgramClassFileData(getTransformedClasses())
.run(parameters.getRuntime(), MAIN_CLASS)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void testDesugaring() throws Exception {
testForDesugaring(parameters)
.addProgramClasses(MAIN_CLASS)
.addProgramClassFileData(getTransformedClasses())
.run(parameters.getRuntime(), MAIN_CLASS)
.applyIf(
// When not desugaring the CF code requires JDK 11.
DesugarTestConfiguration::isNotDesugared,
r -> {
if (parameters.isCfRuntime()
&& parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
r.assertSuccessWithOutput(EXPECTED_OUTPUT);
} else {
r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
}
})
.applyIf(
DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
}
@Test
public void testR8() throws Exception {
assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
testForR8(parameters.getBackend())
.addProgramClasses(MAIN_CLASS)
.addProgramClassFileData(getTransformedClasses())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(MAIN_CLASS)
// TODO(b/198142613): There should not be a warnings on class references which are
// desugared away.
.applyIf(
parameters.getApiLevel().isLessThan(AndroidApiLevel.O),
b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup"))
// TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
.applyIf(
parameters.isCfRuntime(),
b -> {
assertThrows(
CompilationFailedException.class,
() ->
b.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics.assertOnlyErrors();
diagnostics.assertErrorsMatch(
diagnosticMessage(
containsString(
"Unsupported dynamic constant (not desugaring)")));
}));
},
b ->
b.run(parameters.getRuntime(), MAIN_CLASS)
.assertSuccessWithOutput(EXPECTED_OUTPUT));
}
private byte[] getTransformedClasses() throws Exception {
return transformer(I.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
"condy1", I.class, "myConstant", "constantName", Object.class)
.transformConstStringToConstantDynamic(
"condy2", I.class, "myConstant", "constantName", Object.class)
.setPrivate(
I.class.getDeclaredMethod(
"myConstant", MethodHandles.Lookup.class, String.class, Class.class))
.transform();
}
public interface I {
default Object f() {
return "condy1"; // Will be transformed to Constant_DYNAMIC.
}
default Object g() {
return "condy2"; // Will be transformed to Constant_DYNAMIC.
}
/* private */ static Object myConstant(
MethodHandles.Lookup lookup, String name, Class<?> type) {
return new Object();
}
}
public static class A implements I {
public static void main(String[] args) {
A a = new A();
System.out.println(a.f() != null);
System.out.println(a.f() == a.g());
}
}
}