blob: 566b0a0e6385780df4292c75332fddd9c3e60d2a [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.desugar.desugaredlibrary;
import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_8_LIB_JAR;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
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 ProgramRewritingTest extends DesugaredLibraryTestBase {
private static final String TEST_CLASS = "stream.ProgramRewritingTestClass";
private final TestParameters parameters;
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
private final CompilationSpecification compilationSpecification;
@Parameters(name = "{0}, spec: {1}, {2}")
public static List<Object[]> data() {
LibraryDesugaringSpecification jdk8CoreLambdaStubs =
new LibraryDesugaringSpecification(
"JDK8_CL",
ImmutableSet.of(
DESUGARED_JDK_8_LIB_JAR,
ToolHelper.DESUGAR_LIB_CONVERSIONS,
ToolHelper.getCoreLambdaStubs()),
JDK8.getSpecification(),
ImmutableSet.of(ToolHelper.getAndroidJar(AndroidApiLevel.O)),
LibraryDesugaringSpecification.JDK8_DESCRIPTOR,
"");
LibraryDesugaringSpecification jdk11CoreLambdaStubs =
new LibraryDesugaringSpecification(
"JDK11_CL",
ImmutableSet.of(
ToolHelper.getUndesugaredJdk11LibJarForTesting(),
ToolHelper.DESUGAR_LIB_CONVERSIONS,
ToolHelper.getCoreLambdaStubs()),
JDK11.getSpecification(),
ImmutableSet.of(ToolHelper.getAndroidJar(AndroidApiLevel.R)),
LibraryDesugaringSpecification.JDK11_DESCRIPTOR,
"");
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
ImmutableList.of(JDK8, JDK11, jdk8CoreLambdaStubs, jdk11CoreLambdaStubs),
DEFAULT_SPECIFICATIONS);
}
public ProgramRewritingTest(
TestParameters parameters,
LibraryDesugaringSpecification libraryDesugaringSpecification,
CompilationSpecification compilationSpecification) {
this.parameters = parameters;
this.libraryDesugaringSpecification = libraryDesugaringSpecification;
this.compilationSpecification = compilationSpecification;
}
@Test
public void testRewriting() throws Throwable {
Box<String> keepRules = new Box<>();
SingleTestRunResult<?> run =
testForDesugaredLibrary(
parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramFiles(Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "stream.jar"))
.addKeepMainRule(TEST_CLASS)
.applyIf(
compilationSpecification.isProgramShrink(),
b ->
b.addOptionsModification(
options -> {
// TODO(b/140233505): Allow devirtualization once fixed.
options.enableDevirtualization = false;
}))
.compile()
.inspect(this::checkRewrittenInvokes)
.inspectKeepRules(
kr -> {
if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
keepRules.set(String.join("\n", kr));
} else {
assert kr == null;
keepRules.set("");
}
})
.run(parameters.getRuntime(), TEST_CLASS);
assertResultIsCorrect(run.getStdOut(), run.getStdErr(), keepRules.get());
}
private void assertResultIsCorrect(String stdOut, String stdErr, String keepRules) {
if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
if (compilationSpecification.isL8Shrink()) {
assertGeneratedKeepRulesAreCorrect(keepRules);
} else {
// When shrinking the class names are not printed correctly anymore due to minification.
assertLines2By2Correct(stdOut);
}
}
if (parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
// Flaky: There might be a missing method on lambda deserialization.
assertTrue(
!stdErr.contains("Could not find method")
|| stdErr.contains("Could not find method java.lang.invoke.SerializedLambda"));
} else {
assertFalse(stdErr.contains("Could not find method"));
}
}
private void checkRewrittenInvokes(CodeInspector inspector) {
if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
return;
}
ClassSubject classSubject = inspector.clazz(TEST_CLASS);
assertThat(classSubject, isPresent());
List<InstructionSubject> invokes =
classSubject
.uniqueMethodWithName("main")
.streamInstructions()
.filter(instr -> instr.isInvokeInterface() || instr.isInvokeStatic())
.collect(Collectors.toList());
assertInvokeStaticMatching(invokes, 0, "Set$-EL;->spliterator");
assertInvokeStaticMatching(invokes, 1, "Collection$-EL;->stream");
assertInvokeInterfaceMatching(invokes, 2, "Set;->iterator");
assertInvokeStaticMatching(invokes, 3, "Collection$-EL;->stream");
assertInvokeStaticMatching(invokes, 4, "Set$-EL;->spliterator");
assertInvokeInterfaceMatching(invokes, 8, "Iterator;->remove");
assertInvokeStaticMatching(invokes, 9, "DesugarArrays;->spliterator");
assertInvokeStaticMatching(invokes, 10, "DesugarArrays;->spliterator");
assertInvokeStaticMatching(invokes, 11, "DesugarArrays;->stream");
assertInvokeStaticMatching(invokes, 12, "DesugarArrays;->stream");
assertInvokeStaticMatching(invokes, 13, "Collection$-EL;->stream");
assertInvokeStaticMatching(invokes, 14, "IntStream$-CC;->range");
assertInvokeStaticMatching(invokes, 16, "Comparator$-CC;->comparingInt");
assertInvokeStaticMatching(invokes, 17, "List$-EL;->sort");
assertInvokeStaticMatching(invokes, 19, "Comparator$-CC;->comparingInt");
assertInvokeStaticMatching(invokes, 20, "List$-EL;->sort");
assertInvokeStaticMatching(invokes, 21, "Collection$-EL;->stream");
assertEquals(22, invokes.size());
}
private void assertInvokeInterfaceMatching(List<InstructionSubject> invokes, int i, String s) {
assertTrue(invokes.get(i).isInvokeInterface());
assertTrue(invokes.get(i).toString().contains(s));
}
private void assertInvokeStaticMatching(List<InstructionSubject> invokes, int i, String s) {
assertTrue(invokes.get(i).isInvokeStatic());
assertTrue(invokes.get(i).toString().contains(s));
}
private void assertGeneratedKeepRulesAreCorrect(String keepRules) {
String prefix = libraryDesugaringSpecification.functionPrefix(parameters);
String expectedResult =
StringUtils.lines(
"-keep class j$.util.Collection$-EL {",
" j$.util.stream.Stream stream(java.util.Collection);",
"}",
"-keep class j$.util.Comparator$-CC {",
" java.util.Comparator comparingInt(" + prefix + ".util.function.ToIntFunction);",
"}",
"-keep class j$.util.DesugarArrays {",
" j$.util.Spliterator spliterator(java.lang.Object[]);",
" j$.util.Spliterator spliterator(java.lang.Object[], int, int);",
" j$.util.stream.Stream stream(java.lang.Object[]);",
" j$.util.stream.Stream stream(java.lang.Object[], int, int);",
"}",
"-keep class j$.util.List$-EL {",
" void sort(java.util.List, java.util.Comparator);",
"}",
"-keep class j$.util.Set$-EL {",
" j$.util.Spliterator spliterator(java.util.Set);",
"}",
"-keep class j$.util.Spliterator",
"-keep class " + prefix + ".util.function.ToIntFunction { *; }",
"-keep class j$.util.stream.IntStream$-CC {",
" j$.util.stream.IntStream range(int, int);",
"}",
"-keep class j$.util.stream.IntStream",
"-keep class j$.util.stream.Stream");
assertEquals(expectedResult.trim(), keepRules.trim());
}
}