| // 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.metadata; |
| |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; |
| import static org.hamcrest.CoreMatchers.anyOf; |
| import static org.hamcrest.CoreMatchers.equalTo; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.KotlinTestParameters; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.shaking.ProguardKeepAttributes; |
| 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.KmPackageSubject; |
| import java.nio.file.Path; |
| import java.util.Collection; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| @RunWith(Parameterized.class) |
| public class MetadataRewriteInLibraryTypeTest extends KotlinMetadataTestBase { |
| private static final String EXPECTED = StringUtils.lines("Sub::foo", "Sub::boo", "true"); |
| |
| private final TestParameters parameters; |
| |
| @Parameterized.Parameters(name = "{0}, {1}") |
| public static Collection<Object[]> data() { |
| return buildParameters( |
| getTestParameters().withCfRuntimes().build(), |
| getKotlinTestParameters().withAllCompilersAndTargetVersions().build()); |
| } |
| |
| public MetadataRewriteInLibraryTypeTest( |
| TestParameters parameters, KotlinTestParameters kotlinParameters) { |
| super(kotlinParameters); |
| this.parameters = parameters; |
| } |
| |
| private static final KotlinCompileMemoizer baseLibJarMap = |
| getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/libtype_lib_base", "base")); |
| private static final KotlinCompileMemoizer extLibJarMap = |
| getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/libtype_lib_ext", "ext")) |
| .configure( |
| kotlinCompilerTool -> { |
| kotlinCompilerTool.addClasspathFiles( |
| baseLibJarMap.getForConfiguration( |
| kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); |
| }); |
| private static final KotlinCompileMemoizer appJarMap = |
| getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/libtype_app", "main")) |
| .configure( |
| kotlinCompilerTool -> { |
| kotlinCompilerTool.addClasspathFiles( |
| baseLibJarMap.getForConfiguration( |
| kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); |
| kotlinCompilerTool.addClasspathFiles( |
| extLibJarMap.getForConfiguration( |
| kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); |
| }); |
| |
| @Test |
| public void smokeTest() throws Exception { |
| testForJvm() |
| .addRunClasspathFiles( |
| kotlinc.getKotlinStdlibJar(), baseLibJarMap.getForConfiguration(kotlinc, targetVersion)) |
| .addClasspath( |
| extLibJarMap.getForConfiguration(kotlinc, targetVersion), |
| appJarMap.getForConfiguration(kotlinc, targetVersion)) |
| .run(parameters.getRuntime(), PKG + ".libtype_app.MainKt") |
| .assertSuccessWithOutput(EXPECTED); |
| } |
| |
| @Test |
| public void testR8() throws Exception { |
| String main = PKG + ".libtype_app.MainKt"; |
| Path out = |
| testForR8(parameters.getBackend()) |
| // Intentionally not providing baseLibJar as lib file nor classpath file. |
| .addProgramFiles( |
| extLibJarMap.getForConfiguration(kotlinc, targetVersion), |
| appJarMap.getForConfiguration(kotlinc, targetVersion), |
| kotlinc.getKotlinAnnotationJar()) |
| // Keep Ext extension method which requires metadata to be called with Kotlin syntax |
| // from other kotlin code. |
| .addKeepRules("-keep class **.ExtKt { <methods>; }") |
| // Keep the main entry. |
| .addKeepMainRule(main) |
| .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) |
| .addDontWarn(PKG + ".**") |
| .allowDiagnosticWarningMessages() |
| // -dontoptimize so that basic code structure is kept. |
| .addDontOptimize() |
| .compile() |
| .inspect(this::inspect) |
| .assertAllWarningMessagesMatch( |
| anyOf( |
| equalTo("Resource 'META-INF/MANIFEST.MF' already exists."), |
| equalTo("Resource 'META-INF/main.kotlin_module' already exists."))) |
| .writeToZip(); |
| |
| testForJvm() |
| .addRunClasspathFiles( |
| kotlinc.getKotlinStdlibJar(), baseLibJarMap.getForConfiguration(kotlinc, targetVersion)) |
| .addClasspath(out) |
| .run(parameters.getRuntime(), main) |
| .assertSuccessWithOutput(EXPECTED); |
| } |
| |
| private void inspect(CodeInspector inspector) { |
| String extClassName = PKG + ".libtype_lib_ext.ExtKt"; |
| ClassSubject ext = inspector.clazz(extClassName); |
| assertThat(ext, isPresentAndNotRenamed()); |
| // API entry is kept, hence the presence of Metadata. |
| KmPackageSubject kmPackage = ext.getKmPackage(); |
| assertThat(kmPackage, isPresent()); |
| // Type appearance of library type, Base, should be kept, even if it's not provided. |
| // Note that the resulting ClassSubject for Base is an absent one as we don't provide it, and |
| // thus we can't use `getReturnTypesInFunctions`, which filters out absent class subject. |
| assertTrue(kmPackage.getReturnTypeDescriptorsInFunctions().stream().anyMatch( |
| returnType -> returnType.contains("Base"))); |
| } |
| } |