blob: 055671843987b64d648424c9b2538df663a859f0 [file] [log] [blame]
// 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.kotlin.lambda.b159688129;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
import java.util.List;
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 LambdaSplitByCodeCorrectnessTest extends KotlinTestBase {
private final TestParameters parameters;
private final boolean splitGroup;
@Parameters(name = "{0}, {1}, splitGroup: {2}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimesAndAllApiLevels().build(),
getKotlinTestParameters().withAllCompilersLambdaGenerationsAndTargetVersions().build(),
BooleanUtils.values());
}
public LambdaSplitByCodeCorrectnessTest(
TestParameters parameters, KotlinTestParameters kotlinParameters, boolean splitGroup) {
super(kotlinParameters);
this.parameters = parameters;
this.splitGroup = splitGroup;
}
@Test
public void testSplitLambdaGroups() throws Exception {
String PKG_NAME = LambdaSplitByCodeCorrectnessTest.class.getPackage().getName();
String folder = DescriptorUtils.getBinaryNameFromJavaType(PKG_NAME);
CfRuntime cfRuntime =
parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9();
Path ktClasses =
kotlinc(cfRuntime, kotlinParameters)
.addSourceFiles(getKotlinFileInTest(folder, "Simple"))
.compile();
testForR8(parameters.getBackend())
.addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
.addProgramFiles(ktClasses)
.setMinApi(parameters)
.addKeepMainRule(PKG_NAME + ".SimpleKt")
.applyIf(
splitGroup,
b ->
b.addOptionsModification(
internalOptions ->
// Setting inliningInstructionAllowance = 1 will force each switch branch to
// contain an invoke instruction to a private method.
internalOptions.inlinerOptions().inliningInstructionAllowance = 1))
.addHorizontallyMergedClassesInspector(
inspector ->
inspector
.applyIf(
kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_8_0)
|| splitGroup,
i -> {
if (kotlinParameters.getLambdaGeneration().isClass()) {
i.assertIsCompleteMergeGroup(
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$1",
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$2",
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$3",
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$4",
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$5",
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$6")
.assertNoOtherClassesMerged();
} else {
if (parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)
&& !splitGroup) {
inspector.assertNoClassesMerged();
} else {
ClassReference simpleKt =
Reference.classFromTypeName(
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt");
inspector
.assertIsCompleteMergeGroup(
SyntheticItemsTestUtils.syntheticLambdaClass(simpleKt, 0),
SyntheticItemsTestUtils.syntheticLambdaClass(simpleKt, 1),
SyntheticItemsTestUtils.syntheticLambdaClass(simpleKt, 2),
SyntheticItemsTestUtils.syntheticLambdaClass(simpleKt, 3),
SyntheticItemsTestUtils.syntheticLambdaClass(simpleKt, 4),
SyntheticItemsTestUtils.syntheticLambdaClass(simpleKt, 5))
.assertNoOtherClassesMerged();
}
}
})
.assertNoOtherClassesMerged())
.allowDiagnosticWarningMessages()
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.inspectIf(
kotlinParameters.getLambdaGeneration().isClass(),
codeInspector -> {
ClassSubject mergeTarget =
codeInspector.clazz(
"com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$1");
assertThat(
mergeTarget,
isPresentIf(
kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_8_0)
|| splitGroup));
if (mergeTarget.isAbsent()) {
return;
}
boolean isKotlinOld =
kotlinc.isOneOf(
KotlinCompilerVersion.KOTLINC_1_3_72, KotlinCompilerVersion.KOTLINC_1_4_20);
MethodSubject virtualMethodSubject =
mergeTarget.uniqueMethodThatMatches(
method ->
method.isVirtual()
&& !method.isSynthetic()
&& method
.getOriginalMethodName()
.equals(isKotlinOld ? "invoke$1" : "invoke"));
assertThat(virtualMethodSubject, isPresent());
int found = 0;
for (FoundMethodSubject directMethodSubject :
mergeTarget.allMethods(x -> x.isPrivate() && !x.isSynthetic())) {
assertThat(virtualMethodSubject, invokesMethod(directMethodSubject));
found++;
}
assertEquals(splitGroup ? 6 : 0, found);
})
.run(parameters.getRuntime(), PKG_NAME + ".SimpleKt")
.assertSuccessWithOutputLines("Hello1", "Hello2", "Hello3", "Hello4", "Hello5", "Hello6");
}
}