blob: 463efbb94b60d1dafc08a1180d0c2b992b5cd4a5 [file] [log] [blame]
// 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.classinliner;
import static com.android.tools.r8.references.Reference.methodFromMethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
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;
@RunWith(Parameterized.class)
public class LibraryOverrideClassInliningTest extends TestBase {
@NoHorizontalClassMerging
public static class SimpleLibraryOverride implements Runnable {
@NeverInline
@Override
public void run() {
Main.println("running...");
}
}
public static class NonSimpleLibraryOverride implements Runnable {
@NeverInline
@Override
public void run() {
char[] buffer = new char[System.nanoTime() < 0 ? (int) System.nanoTime() : 100];
String greeting = "Hello world!";
for (int i = 0; i < greeting.length(); i++) {
buffer[i] = greeting.charAt(i);
}
StringBuilder result = new StringBuilder();
int j = 0;
for (char c : greeting.toCharArray()) {
result.append(buffer[j++]);
}
Main.println(result.toString());
}
}
public static class Main {
// Print method to ensure an instruction count within the max 3.
@NeverInline
public static void println(String string) {
System.out.println(string);
}
public static void main(String[] args) {
// Potential class inlining candidate.
new SimpleLibraryOverride().run();
new NonSimpleLibraryOverride().run();
// Escaping usage of the class ensuring the library method must be kept.
if (System.nanoTime() < 0) {
System.out.println(new SimpleLibraryOverride());
System.out.println(new NonSimpleLibraryOverride());
}
}
}
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public final TestParameters parameters;
public LibraryOverrideClassInliningTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void test() throws Exception {
CodeInspector inspector =
testForR8(parameters.getBackend())
.enableInliningAnnotations()
.addDontObfuscate()
.addProgramClasses(
Main.class, SimpleLibraryOverride.class, NonSimpleLibraryOverride.class)
.addKeepMainRule(Main.class)
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("running...", "Hello world!")
.inspector();
assertThat(inspector.clazz(SimpleLibraryOverride.class), isPresent());
assertThat(inspector.clazz(NonSimpleLibraryOverride.class), isPresent());
MethodSubject main =
inspector.method(methodFromMethod(Main.class.getMethod("main", String[].class)));
// Check the simple run() is inlined.
assertTrue(
"Unexpected Simple.run invoke in:\n" + main.getMethod().codeToString(),
main.streamInstructions()
.noneMatch(
i ->
i.isInvoke()
&& i.getMethod().name.toString().equals("run")
&& i.getMethod()
.holder
.toString()
.equals(SimpleLibraryOverride.class.getTypeName())));
// TODO(b/181942160): The non-simple run should ideally not inlined independent of the class
// inliner instruction allowance, since there is an instance of NonSimpleLibraryOverride that
// is not eligible for class inlining.
assertEquals(
"Expected NonSimple.run invoke in:\n" + main.getMethod().codeToString(),
parameters.isDexRuntime(),
main.streamInstructions()
.anyMatch(
i ->
i.isInvoke()
&& i.getMethod().name.toString().equals("run")
&& i.getMethod()
.holder
.toString()
.equals(NonSimpleLibraryOverride.class.getTypeName())));
}
}