blob: 61b75dea0754469b98c025feed968a9ad754447d [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.lambdas;
import static com.android.tools.r8.utils.codeinspector.Matchers.isNative;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPrivate;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
import java.util.function.Function;
import org.junit.BeforeClass;
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;
// See b/234475018) for context.
@RunWith(Parameterized.class)
public class LambdaMethodsWithModifiedAccessTest extends TestBase {
@Parameter() public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
private static final String LAMBDA_TO_PUBLIC = "lambda$withPublicLambdaMethod$0";
private static final String LAMBDA_TO_NATIVE = "lambda$withNativeLambdaMethod$1";
private static final String EXPECTED_OUTPUT =
StringUtils.lines("withPublicLambdaMethod", "UnsatisfiedLinkError: withNativeLambdaMethod");
@BeforeClass
public static void checkJavacLambdas() throws IOException {
CodeInspector inspector =
new CodeInspector(ToolHelper.getClassFileForTestClass(LambdaTest.class));
inspector.forAllClasses(clazz -> clazz.forAllMethods(System.out::println));
assertThat(
inspector.clazz(LambdaTest.class).uniqueMethodWithName(LAMBDA_TO_PUBLIC), isPrivate());
assertThat(
inspector.clazz(LambdaTest.class).uniqueMethodWithName(LAMBDA_TO_NATIVE), isPrivate());
}
private void inspect(CodeInspector inspector) {
assertThat(
inspector.clazz(LambdaTest.class).uniqueMethodWithName(LAMBDA_TO_PUBLIC), isPublic());
assertThat(
inspector.clazz(LambdaTest.class).uniqueMethodWithName(LAMBDA_TO_NATIVE), isNative());
}
@Test
public void testJvm() throws Exception {
assumeTrue(parameters.isCfRuntime());
testForJvm()
.addProgramClasses(TestClass.class)
.addProgramClassFileData(getTransformedLambdaTest())
.run(parameters.getRuntime(), TestClass.class)
.inspect(this::inspect)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
// TODO(b/234475018): Compilation hits an AssertionError which checks expected javac generated
// lambda methods in LambdaDescriptor.lookupTargetMethod.
// assert target == null
// || (implHandle.type.isInvokeInstance() && isInstanceMethod(target))
// || (implHandle.type.isInvokeDirect() && isPrivateInstanceMethod(target))
// || (implHandle.type.isInvokeDirect() && isPublicizedInstanceMethod(target));
@Test(expected = CompilationFailedException.class)
public void testD8() throws Exception {
assumeTrue(parameters.isDexRuntime());
testForD8(parameters.getBackend())
.addProgramClasses(TestClass.class)
.addProgramClassFileData(getTransformedLambdaTest())
.setMinApi(parameters.getApiLevel())
.compile();
}
// TODO(b/234475018): Compilation hits an AssertionError which checks expected javac generated
// lambda methods in LambdaDescriptor.lookupTargetMethod.
// assert target == null
// || (implHandle.type.isInvokeInstance() && isInstanceMethod(target))
// || (implHandle.type.isInvokeDirect() && isPrivateInstanceMethod(target))
// || (implHandle.type.isInvokeDirect() && isPublicizedInstanceMethod(target));
@Test(expected = CompilationFailedException.class)
public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class)
.addProgramClassFileData(getTransformedLambdaTest())
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.compile();
}
private byte[] getTransformedLambdaTest() throws Exception {
return transformer(LambdaTest.class)
.setAccessFlags(MethodPredicate.onName(LAMBDA_TO_NATIVE), MethodAccessFlags::setNative)
.setAccessFlags(MethodPredicate.onName(LAMBDA_TO_PUBLIC), AccessFlags::promoteToPublic)
.removeMethodsCodeAndAnnotations(MethodPredicate.onName(LAMBDA_TO_NATIVE))
.transform();
}
static class TestClass {
public static void main(String[] args) {
new LambdaTest().withPublicLambdaMethod().apply(null);
try {
new LambdaTest().withNativeLambdaMethod().apply(null);
} catch (UnsatisfiedLinkError e) {
if (e.getMessage().contains(LAMBDA_TO_NATIVE)) {
System.out.println("UnsatisfiedLinkError: withNativeLambdaMethod");
} else {
System.out.println("UnsatisfiedLinkError: with unexpected content");
}
}
}
}
public static class LambdaTest {
String f;
Function<Void, String> withPublicLambdaMethod() {
return (ignored) -> {
System.out.println("withPublicLambdaMethod");
return f;
};
}
Function<Void, String> withNativeLambdaMethod() {
return (ignored) -> f;
}
}
}