blob: 21ed11f1a7c2d0c83ef8b86c84dffbac7df5e19e [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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.tools.r8.DesugarTestConfiguration;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
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 LambdaFactoryTest extends TestBase {
@Parameter() public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
private static final String EXPECTED_OUTPUT = StringUtils.lines("1", "2", "3", "4.0", "5");
private boolean isLambdaFactoryMethod(MethodSubject method) {
return method.isSynthetic() && method.isStatic() && method.getFinalName().equals("create");
}
private boolean isInvokingLambdaFactoryMethod(InstructionSubject instruction) {
return instruction.isInvokeStatic()
&& SyntheticItemsTestUtils.isExternalSynthetic(
instruction.getMethod().getHolderType().asClassReference())
&& instruction.getMethod().getName().toString().equals("create");
}
private void inspectDesugared(CodeInspector inspector) {
inspector.forAllClasses(
clazz -> {
if (SyntheticItemsTestUtils.isExternalSynthetic(clazz.getFinalReference())) {
assertTrue(clazz.allMethods().stream().anyMatch(this::isLambdaFactoryMethod));
}
});
assertEquals(
3,
inspector
.clazz(TestClass.class)
.mainMethod()
.streamInstructions()
.filter(this::isInvokingLambdaFactoryMethod)
.count());
}
private void inspectNotDesugared(CodeInspector inspector) {
inspector.forAllClasses(
clazz -> {
if (SyntheticItemsTestUtils.isExternalSynthetic(clazz.getFinalReference())) {
assertTrue(clazz.allMethods().stream().noneMatch(this::isLambdaFactoryMethod));
}
});
assertEquals(
0,
inspector
.clazz(TestClass.class)
.mainMethod()
.streamInstructions()
.filter(this::isInvokingLambdaFactoryMethod)
.count());
}
@Test
public void testDesugaring() throws Exception {
testForDesugaring(
parameters,
options -> {
options.testing.alwaysGenerateLambdaFactoryMethods = true;
})
.addInnerClasses(getClass())
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
DesugarTestConfiguration::isDesugared,
r -> {
try {
r.inspect(this::inspectDesugared);
} catch (Exception e) {
fail();
}
},
r -> {
try {
r.inspect(this::inspectNotDesugared);
} catch (Exception e) {
fail();
}
})
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.inspect(
inspector -> {
if (parameters.isDexRuntime()) {
// Lambdas are fully inlined when desugaring.
assertEquals(1, inspector.allClasses().size());
assertEquals(1, inspector.clazz(TestClass.class).allMethods().size());
}
})
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
interface MyConsumer<T> {
void create(T o);
}
interface MyTriConsumer<T, U, V> {
void accept(T o1, U o2, V o3);
}
static class TestClass {
public static void greet() {
System.out.println("1");
}
public static void greet(MyConsumer<String> consumer) {
consumer.create("2");
}
public static void greetTri(long l, double d, String s) {
System.out.println(l);
System.out.println(d);
System.out.println(s);
}
public static void main(String[] args) throws Exception {
((Runnable) TestClass::greet).run();
greet(System.out::println);
((MyTriConsumer<Long, Double, String>) TestClass::greetTri).accept(3L, 4.0, "5");
}
}
}