| // Copyright (c) 2019, 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.ir.optimize.inliner; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.TestParametersCollection; |
| import com.android.tools.r8.TestRuntime; |
| import com.android.tools.r8.ToolHelper.DexVm; |
| import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject; |
| 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.Parameters; |
| |
| // Test whether a single-caller method, called through a single-target library interface, |
| // is inlined. |
| @RunWith(Parameterized.class) |
| public class InlineLibraryInterfaceMethodTest extends TestBase { |
| |
| @Parameters(name = "{0}") |
| public static TestParametersCollection data() { |
| return getTestParameters().withDexRuntime(DexVm.ART_DEFAULT.getVersion()).build(); |
| } |
| |
| private final TestParameters testParameters; |
| |
| public InlineLibraryInterfaceMethodTest(TestParameters testParameters) { |
| this.testParameters = testParameters; |
| } |
| |
| @Test |
| public void test() throws Exception { |
| class Counts { |
| private long run = 0; |
| private long println = 0; |
| } |
| Counts counts = new Counts(); |
| TestRuntime testRuntime = testParameters.getRuntime(); |
| testForR8(Backend.DEX) |
| .addInnerClasses(InlineLibraryInterfaceMethodTest.class) |
| .addKeepMainRule(TestClass.class) |
| .setMinApi(testRuntime) |
| .noMinification() |
| .run(testRuntime, TestClass.class) |
| .assertSuccess() |
| .inspect( |
| inspector -> { |
| MethodSubject methodSubject = |
| inspector.clazz(TestClass.class).uniqueMethodWithName("main").asMethodSubject(); |
| |
| counts.run = countInvokesWithName(methodSubject, "run"); |
| counts.println = countInvokesWithName(methodSubject, "println"); |
| }); |
| |
| // TODO(b/126323172) Add test here to check the same with desugared lambdas. |
| assertEquals(0, counts.run); |
| assertEquals(2, counts.println); |
| } |
| |
| private static long countInvokesWithName(MethodSubject methodSubject, String name) { |
| return methodSubject |
| .streamInstructions() |
| .filter( |
| instruction -> |
| instruction.isInvoke() |
| && ((InvokeInstructionSubject) instruction) |
| .invokedMethod() |
| .name |
| .toString() |
| .equals(name)) |
| .count(); |
| } |
| |
| // The test classes imitate desugared lambda classes. |
| static class StatelessLambdaClass implements Runnable { |
| public static final StatelessLambdaClass instance = new StatelessLambdaClass(); |
| |
| @Override |
| public void run() { |
| TestClass.statelessLambdaImpl(); |
| } |
| } |
| |
| static class StatefulLambdaClass implements Runnable { |
| final String s; |
| |
| StatefulLambdaClass(String s) { |
| this.s = s; |
| } |
| |
| @Override |
| public void run() { |
| TestClass.statefulLambdaImpl(s); |
| } |
| } |
| |
| static class TestClass { |
| public static int counter = 0; |
| |
| static void statelessLambdaImpl() { |
| System.out.println("Running stateless lambda."); |
| ++counter; |
| } |
| |
| static void statefulLambdaImpl(String s) { |
| System.out.println("Running stateful lambda: " + s + "."); |
| counter += 10; |
| } |
| |
| public static void main(String[] args) { |
| Runnable runnable = StatelessLambdaClass.instance; |
| runnable.run(); |
| |
| String s = "lambda-state"; |
| runnable = new StatefulLambdaClass(s); |
| runnable.run(); |
| |
| if (counter != 11) { |
| throw new RuntimeException("Counter is " + counter + ", expected: 11."); |
| } |
| } |
| } |
| } |