| // 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.lambdas; |
| |
| import static org.junit.Assume.assumeTrue; |
| |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.TestParametersCollection; |
| import com.android.tools.r8.references.Reference; |
| import com.android.tools.r8.utils.StringUtils; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| /** |
| * These tests document the behavior of lambdas w.r.t identity and equality. |
| * |
| * <p>The D8 and R8 compilers take the stance that a program should not rely on either identity or |
| * equality of any lambda metafactory allocated lambda. Thus the status of these tests differ |
| * between JVM, D8/CF, D8/DEX and R8 runs as the compilers may or may not share classes and |
| * allocations as seen fit. |
| */ |
| @RunWith(Parameterized.class) |
| public class LambdaEqualityTest extends TestBase { |
| |
| static final String EXPECTED_JAVAC = |
| StringUtils.lines( |
| "Same method refs", |
| "true", |
| "true", |
| "true", |
| "Different method refs", |
| "false", |
| "false", |
| "false", |
| "Empty lambda", |
| "false", |
| "false", |
| "false"); |
| |
| static final String EXPECTED_D8 = |
| StringUtils.lines( |
| "Same method refs", |
| "true", |
| "true", |
| "true", |
| "Different method refs", |
| "true", // D8 will share the class for the method references. |
| "false", |
| "false", |
| "Empty lambda", |
| "false", |
| "false", |
| "false"); |
| |
| static final String EXPECTED_R8 = |
| StringUtils.lines( |
| "Same method refs", |
| "true", |
| "true", |
| "true", |
| "Different method refs", |
| "true", // R8 will share the class for the method references. |
| "false", |
| "false", |
| "Empty lambda", |
| "true", // R8 will eliminate the call to the impl method thus making lambdas equal. |
| "false", |
| "false"); |
| |
| private final TestParameters parameters; |
| |
| @Parameterized.Parameters(name = "{0}") |
| public static TestParametersCollection data() { |
| return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); |
| } |
| |
| public LambdaEqualityTest(TestParameters parameters) { |
| this.parameters = parameters; |
| } |
| |
| @Test |
| public void testJvm() throws Exception { |
| assumeTrue(parameters.isCfRuntime()); |
| testForRuntime(parameters) |
| .addInnerClasses(LambdaEqualityTest.class) |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED_JAVAC); |
| } |
| |
| @Test |
| public void testD8() throws Exception { |
| testForD8(parameters.getBackend()) |
| .addInnerClasses(LambdaEqualityTest.class) |
| .setMinApi(parameters.getApiLevel()) |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED_D8); |
| } |
| |
| @Test |
| public void testR8() throws Exception { |
| testForR8(parameters.getBackend()) |
| .addInnerClasses(LambdaEqualityTest.class) |
| .setMinApi(parameters.getApiLevel()) |
| .addKeepMainRule(TestClass.class) |
| .addKeepMethodRules( |
| Reference.methodFromMethod( |
| TestClass.class.getDeclaredMethod( |
| "compare", String.class, MyInterface.class, MyInterface.class))) |
| .run(parameters.getRuntime(), TestClass.class) |
| // The use of invoke dynamics prohibits the optimization and sharing of lambdas in R8. |
| .assertSuccessWithOutput(parameters.isCfRuntime() ? EXPECTED_JAVAC : EXPECTED_R8); |
| } |
| |
| interface MyInterface { |
| void foo(); |
| } |
| |
| static class TestClass { |
| |
| public static void compare(String msg, MyInterface i1, MyInterface i2) { |
| System.out.println(msg); |
| System.out.println(i1.getClass() == i2.getClass()); |
| System.out.println(i1 == i2); |
| System.out.println(i1.equals(i2)); |
| } |
| |
| public static void main(String[] args) { |
| MyInterface println = System.out::println; |
| // These lambdas are physically the same and should remain so in all cases. |
| compare("Same method refs", println, println); |
| // These lambdas can be shared as they reference the same actual function. |
| compare("Different method refs", println, System.out::println); |
| // These lambdas cannot be shared (by D8) as javac will generate a lambda$main$X for each. |
| compare("Empty lambda", () -> {}, () -> {}); |
| } |
| } |
| } |