From bcee270265216647ca3b401c06272b4adfa8a058 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Tue, 28 Mar 2023 19:32:44 +0200 Subject: [PATCH] [PL][tests] Add JS K2 ABI partial linkage tests --- .../generators/tests/GenerateJsTests.kt | 9 +- .../ir/AbstractJsPartialLinkageTestCase.kt | 200 ++++++++++++------ ...JsPartialLinkageNoICTestCaseGenerated.java | 132 ++++++++++++ 3 files changed, 276 insertions(+), 65 deletions(-) create mode 100644 js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsPartialLinkageNoICTestCaseGenerated.java diff --git a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt index ff265e0c785..4b3047563bb 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt @@ -39,12 +39,17 @@ fun main(args: Array) { testGroup("js/js.tests/tests-gen", "compiler/testData") { testClass { - model("klibABI/", pattern = "^([^_](.+))$", targetBackend = TargetBackend.JS_IR, recursive = false, ) + model("klibABI/", pattern = "^([^_](.+))$", targetBackend = TargetBackend.JS_IR, recursive = false) } } testGroup("js/js.tests/tests-gen", "compiler/testData") { testClass { - model("klibABI/", pattern = "^([^_](.+))$", targetBackend = TargetBackend.JS_IR, recursive = false, ) + model("klibABI/", pattern = "^([^_](.+))$", targetBackend = TargetBackend.JS_IR, recursive = false) + } + } + testGroup("js/js.tests/tests-gen", "compiler/testData") { + testClass { + model("klibABI/", pattern = "^([^_](.+))$", targetBackend = TargetBackend.JS_IR, recursive = false) } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsPartialLinkageTestCase.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsPartialLinkageTestCase.kt index d398b47d42c..99f4e5a8681 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsPartialLinkageTestCase.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsPartialLinkageTestCase.kt @@ -12,12 +12,18 @@ import com.intellij.psi.PsiManager import com.intellij.psi.SingleRootFileViewProvider import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport +import org.jetbrains.kotlin.cli.common.messages.MessageRenderer +import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector +import org.jetbrains.kotlin.cli.js.klib.compileModuleToAnalyzedFir import org.jetbrains.kotlin.cli.js.klib.generateIrForKlibSerialization +import org.jetbrains.kotlin.cli.js.klib.serializeFirKlib +import org.jetbrains.kotlin.cli.js.klib.transformFirToIr import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity import org.jetbrains.kotlin.ir.backend.js.ic.CacheUpdater @@ -31,25 +37,29 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.testOld.V8IrJsTestChecker import org.jetbrains.kotlin.klib.PartialLinkageTestUtils +import org.jetbrains.kotlin.klib.PartialLinkageTestUtils.Dependencies import org.jetbrains.kotlin.klib.PartialLinkageTestUtils.MAIN_MODULE_NAME import org.jetbrains.kotlin.konan.file.ZipFileSystemCacheableAccessor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase +import java.io.ByteArrayOutputStream import java.io.File +import java.io.PrintStream +import java.nio.charset.Charset import kotlin.io.path.createTempDirectory -abstract class AbstractJsPartialLinkageWithICTestCase : AbstractJsPartialLinkageTestCase() { - override val useIncrementalCompiler get() = true -} +abstract class AbstractJsPartialLinkageNoICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K1_NO_IC) +abstract class AbstractJsPartialLinkageWithICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K1_WITH_IC) +abstract class AbstractFirJsPartialLinkageNoICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K2_NO_IC) -abstract class AbstractJsPartialLinkageNoICTestCase : AbstractJsPartialLinkageTestCase() { - override val useIncrementalCompiler get() = false -} - -abstract class AbstractJsPartialLinkageTestCase : KtUsefulTestCase() { - abstract val useIncrementalCompiler: Boolean +abstract class AbstractJsPartialLinkageTestCase(val compilerType: CompilerType) : KtUsefulTestCase() { + enum class CompilerType(val testModeName: String) { + K1_NO_IC("JS_NO_IC"), + K1_WITH_IC("JS_WITH_IC"), + K2_NO_IC("JS_NO_IC") + } private lateinit var buildDir: File private lateinit var environment: KotlinCoreEnvironment @@ -90,12 +100,16 @@ abstract class AbstractJsPartialLinkageTestCase : KtUsefulTestCase() { override val testDir: File = File(testPath).absoluteFile override val buildDir: File get() = this@AbstractJsPartialLinkageTestCase.buildDir override val stdlibFile: File get() = File("libraries/stdlib/js-ir/build/classes/kotlin/js/main").absoluteFile - override val testModeName = if (this@AbstractJsPartialLinkageTestCase.useIncrementalCompiler) "JS_WITH_IC" else "JS_NO_IC" + override val testModeName get() = this@AbstractJsPartialLinkageTestCase.compilerType.testModeName - override fun buildKlib(moduleName: String, moduleSourceDir: File, dependencies: PartialLinkageTestUtils.Dependencies, klibFile: File) = - this@AbstractJsPartialLinkageTestCase.buildKlib(moduleName, moduleSourceDir, dependencies, klibFile) + override fun buildKlib( + moduleName: String, + moduleSourceDir: File, + dependencies: Dependencies, + klibFile: File + ) = this@AbstractJsPartialLinkageTestCase.buildKlib(moduleName, moduleSourceDir, dependencies, klibFile) - override fun buildBinaryAndRun(mainModuleKlibFile: File, dependencies: PartialLinkageTestUtils.Dependencies) = + override fun buildBinaryAndRun(mainModuleKlibFile: File, dependencies: Dependencies) = this@AbstractJsPartialLinkageTestCase.buildBinaryAndRun(mainModuleKlibFile, dependencies) override fun onNonEmptyBuildDirectory(directory: File) { @@ -111,41 +125,51 @@ abstract class AbstractJsPartialLinkageTestCase : KtUsefulTestCase() { // The entry point to generated test classes. fun doTest(testPath: String) = PartialLinkageTestUtils.runTest(JsTestConfiguration(testPath)) - private fun buildKlib(moduleName: String, moduleSourceDir: File, dependencies: PartialLinkageTestUtils.Dependencies, klibFile: File) { + private fun buildKlib(moduleName: String, moduleSourceDir: File, dependencies: Dependencies, klibFile: File) { + when (compilerType) { + CompilerType.K1_NO_IC, CompilerType.K1_WITH_IC -> buildKlibWithK1(moduleName, moduleSourceDir, dependencies, klibFile) + CompilerType.K2_NO_IC -> buildKlibWithK2(moduleName, moduleSourceDir, dependencies, klibFile) + } + } + + private fun buildKlibWithK1(moduleName: String, moduleSourceDir: File, dependencies: Dependencies, klibFile: File) { val config = createConfig(moduleName) val ktFiles = environment.createPsiFiles(moduleSourceDir) - val sourceModule = prepareAnalyzedSourceModule( + val regularDependencies = dependencies.regularDependencies.map { it.libraryFile.absolutePath } + val friendDependencies = dependencies.friendDependencies.map { it.libraryFile.absolutePath } + + val moduleStructure = prepareAnalyzedSourceModule( environment.project, ktFiles, config, - dependencies.regularDependencies.map { it.libraryFile.path }, - dependencies.friendDependencies.map { it.libraryFile.path }, + regularDependencies, + friendDependencies, AnalyzerWithCompilerReport(config) ) - val moduleSourceFiles = (sourceModule.mainModule as MainModule.SourceFiles).files - val icData = sourceModule.compilerConfiguration.incrementalDataProvider?.getSerializedData(moduleSourceFiles) ?: emptyList() + val moduleSourceFiles = (moduleStructure.mainModule as MainModule.SourceFiles).files + val icData = moduleStructure.compilerConfiguration.incrementalDataProvider?.getSerializedData(moduleSourceFiles) ?: emptyList() val expectDescriptorToSymbol = mutableMapOf() val (moduleFragment, _) = generateIrForKlibSerialization( environment.project, moduleSourceFiles, config, - sourceModule.jsFrontEndResult.jsAnalysisResult, - sortDependencies(sourceModule.moduleDependencies), + moduleStructure.jsFrontEndResult.jsAnalysisResult, + sortDependencies(moduleStructure.moduleDependencies), icData, expectDescriptorToSymbol, IrFactoryImpl, verifySignatures = true ) { - sourceModule.getModuleDescriptor(it) + moduleStructure.getModuleDescriptor(it) } val metadataSerializer = - KlibMetadataIncrementalSerializer(config, sourceModule.project, sourceModule.jsFrontEndResult.hasErrors) + KlibMetadataIncrementalSerializer(config, moduleStructure.project, moduleStructure.jsFrontEndResult.hasErrors) generateKLib( - sourceModule, + moduleStructure, klibFile.path, nopack = false, jsOutputName = moduleName, @@ -153,17 +177,67 @@ abstract class AbstractJsPartialLinkageTestCase : KtUsefulTestCase() { expectDescriptorToSymbol = expectDescriptorToSymbol, moduleFragment = moduleFragment ) { file -> - metadataSerializer.serializeScope(file, sourceModule.jsFrontEndResult.bindingContext, moduleFragment.descriptor) + metadataSerializer.serializeScope(file, moduleStructure.jsFrontEndResult.bindingContext, moduleFragment.descriptor) } } - private fun buildBinaryAndRun(mainModuleKlibFile: File, allDependencies: PartialLinkageTestUtils.Dependencies) { + private fun buildKlibWithK2(moduleName: String, moduleSourceDir: File, dependencies: Dependencies, klibFile: File) { + val config = createConfig(moduleName) + val ktFiles = environment.createPsiFiles(moduleSourceDir) + + val regularDependencies = dependencies.regularDependencies.map { it.libraryFile.absolutePath } + val friendDependencies = dependencies.friendDependencies.map { it.libraryFile.absolutePath } + + val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter() + + val moduleStructure = ModulesStructure( + project = environment.project, + mainModule = MainModule.SourceFiles(ktFiles), + compilerConfiguration = config, + dependencies = regularDependencies, + friendDependenciesPaths = friendDependencies + ) + + val outputStream = ByteArrayOutputStream() + val messageCollector = PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.PLAIN_FULL_PATHS, true) + + val outputs = compileModuleToAnalyzedFir( + moduleStructure = moduleStructure, + ktFiles = ktFiles, + libraries = regularDependencies, + friendLibraries = friendDependencies, + messageCollector = messageCollector, + diagnosticsReporter = diagnosticsReporter + ) + + if (outputs != null) { + val fir2IrActualizedResult = transformFirToIr(moduleStructure, outputs, diagnosticsReporter) + + serializeFirKlib( + moduleStructure = moduleStructure, + firOutputs = outputs, + fir2IrActualizedResult = fir2IrActualizedResult, + outputKlibPath = klibFile.absolutePath, + messageCollector = messageCollector, + diagnosticsReporter = diagnosticsReporter, + jsOutputName = moduleName + ) + } + + if (messageCollector.hasErrors()) { + val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8")) + throw AssertionError("The following errors occurred compiling test:\n$messages") + } + + } + + private fun buildBinaryAndRun(mainModuleKlibFile: File, allDependencies: Dependencies) { val configuration = createConfig(MAIN_MODULE_NAME) - val compilationOutputs = if (useIncrementalCompiler) - buildBinaryWithIC(configuration, mainModuleKlibFile, allDependencies) - else - buildBinaryNoIC(configuration, mainModuleKlibFile, allDependencies) + val compilationOutputs = when (compilerType) { + CompilerType.K1_NO_IC, CompilerType.K2_NO_IC -> buildBinaryNoIC(configuration, mainModuleKlibFile, allDependencies) + CompilerType.K1_WITH_IC -> buildBinaryWithIC(configuration, mainModuleKlibFile, allDependencies) + } val binariesDir = File(buildDir, BIN_DIR_NAME).also { it.mkdirs() } val binaries = compilationOutputs.writeAll(binariesDir, MAIN_MODULE_NAME, false, MAIN_MODULE_NAME, ModuleKind.PLAIN).filter { @@ -173,42 +247,10 @@ abstract class AbstractJsPartialLinkageTestCase : KtUsefulTestCase() { executeAndCheckBinaries(MAIN_MODULE_NAME, binaries) } - private fun buildBinaryWithIC( - configuration: CompilerConfiguration, - mainModuleKlibFile: File, - allDependencies: PartialLinkageTestUtils.Dependencies - ): CompilationOutputs { - // TODO: what about friend dependencies? - val cacheUpdater = CacheUpdater( - mainModule = mainModuleKlibFile.absolutePath, - allModules = allDependencies.regularDependencies.map { it.libraryFile.path }, - mainModuleFriends = emptyList(), - cacheDir = buildDir.resolve("libs-cache").absolutePath, - compilerConfiguration = configuration, - irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) }, - mainArguments = null, - compilerInterfaceFactory = { mainModule, cfg -> - JsIrCompilerWithIC(mainModule, cfg, JsGenerationGranularity.PER_MODULE, PhaseConfig(jsPhases), setOf(BOX_FUN_FQN)) - } - ) - val icCaches = cacheUpdater.actualizeCaches() - - val mainModuleName = icCaches.last().moduleExternalName - val jsExecutableProducer = JsExecutableProducer( - mainModuleName = mainModuleName, - moduleKind = configuration[JSConfigurationKeys.MODULE_KIND]!!, - sourceMapsInfo = SourceMapsInfo.from(configuration), - caches = icCaches, - relativeRequirePath = true - ) - - return jsExecutableProducer.buildExecutable(multiModule = true, outJsProgram = true).compilationOut - } - private fun buildBinaryNoIC( configuration: CompilerConfiguration, mainModuleKlibFile: File, - allDependencies: PartialLinkageTestUtils.Dependencies + allDependencies: Dependencies ): CompilationOutputs { val klib = MainModule.Klib(mainModuleKlibFile.path) val moduleStructure = ModulesStructure( @@ -241,6 +283,38 @@ abstract class AbstractJsPartialLinkageTestCase : KtUsefulTestCase() { return compiledResult.outputs[TranslationMode.PER_MODULE_DEV] ?: error("No compiler output") } + private fun buildBinaryWithIC( + configuration: CompilerConfiguration, + mainModuleKlibFile: File, + allDependencies: Dependencies + ): CompilationOutputs { + // TODO: what about friend dependencies? + val cacheUpdater = CacheUpdater( + mainModule = mainModuleKlibFile.absolutePath, + allModules = allDependencies.regularDependencies.map { it.libraryFile.path }, + mainModuleFriends = emptyList(), + cacheDir = buildDir.resolve("libs-cache").absolutePath, + compilerConfiguration = configuration, + irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) }, + mainArguments = null, + compilerInterfaceFactory = { mainModule, cfg -> + JsIrCompilerWithIC(mainModule, cfg, JsGenerationGranularity.PER_MODULE, PhaseConfig(jsPhases), setOf(BOX_FUN_FQN)) + } + ) + val icCaches = cacheUpdater.actualizeCaches() + + val mainModuleName = icCaches.last().moduleExternalName + val jsExecutableProducer = JsExecutableProducer( + mainModuleName = mainModuleName, + moduleKind = configuration[JSConfigurationKeys.MODULE_KIND]!!, + sourceMapsInfo = SourceMapsInfo.from(configuration), + caches = icCaches, + relativeRequirePath = true + ) + + return jsExecutableProducer.buildExecutable(multiModule = true, outJsProgram = true).compilationOut + } + private fun KotlinCoreEnvironment.createPsiFiles(sourceDir: File): List { val psiManager = PsiManager.getInstance(project) val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsPartialLinkageNoICTestCaseGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsPartialLinkageNoICTestCaseGenerated.java new file mode 100644 index 00000000000..c3a61fae222 --- /dev/null +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsPartialLinkageNoICTestCaseGenerated.java @@ -0,0 +1,132 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.js.test.ir; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateJsTestsKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/klibABI") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class FirJsPartialLinkageNoICTestCaseGenerated extends AbstractFirJsPartialLinkageNoICTestCase { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + @TestMetadata("addEnumEntry") + public void testAddEnumEntry() throws Exception { + runTest("compiler/testData/klibABI/addEnumEntry/"); + } + + @TestMetadata("addSealedSubclass") + public void testAddSealedSubclass() throws Exception { + runTest("compiler/testData/klibABI/addSealedSubclass/"); + } + + public void testAllFilesPresentInKlibABI() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/klibABI"), Pattern.compile("^([^_](.+))$"), null, TargetBackend.JS_IR, false); + } + + @TestMetadata("changeClassVisibility") + public void testChangeClassVisibility() throws Exception { + runTest("compiler/testData/klibABI/changeClassVisibility/"); + } + + @TestMetadata("changeFunctionVisibility") + public void testChangeFunctionVisibility() throws Exception { + runTest("compiler/testData/klibABI/changeFunctionVisibility/"); + } + + @TestMetadata("changePropertyVisibility") + public void testChangePropertyVisibility() throws Exception { + runTest("compiler/testData/klibABI/changePropertyVisibility/"); + } + + @TestMetadata("classTransformations") + public void testClassTransformations() throws Exception { + runTest("compiler/testData/klibABI/classTransformations/"); + } + + @TestMetadata("functionTransformations") + public void testFunctionTransformations() throws Exception { + runTest("compiler/testData/klibABI/functionTransformations/"); + } + + @TestMetadata("inheritanceIssues") + public void testInheritanceIssues() throws Exception { + runTest("compiler/testData/klibABI/inheritanceIssues/"); + } + + @TestMetadata("noNonImplementedCallableFalsePositives") + public void testNoNonImplementedCallableFalsePositives() throws Exception { + runTest("compiler/testData/klibABI/noNonImplementedCallableFalsePositives/"); + } + + @TestMetadata("nonAbstractCallableBecomesAbstract") + public void testNonAbstractCallableBecomesAbstract() throws Exception { + runTest("compiler/testData/klibABI/nonAbstractCallableBecomesAbstract/"); + } + + @TestMetadata("nonExhaustivenessOfWhenClause") + public void testNonExhaustivenessOfWhenClause() throws Exception { + runTest("compiler/testData/klibABI/nonExhaustivenessOfWhenClause/"); + } + + @TestMetadata("propertyTransformations") + public void testPropertyTransformations() throws Exception { + runTest("compiler/testData/klibABI/propertyTransformations/"); + } + + @TestMetadata("referencingUnusableDeclarations") + public void testReferencingUnusableDeclarations() throws Exception { + runTest("compiler/testData/klibABI/referencingUnusableDeclarations/"); + } + + @TestMetadata("removeAbstractCallableFromAbstractClassOrInterface") + public void testRemoveAbstractCallableFromAbstractClassOrInterface() throws Exception { + runTest("compiler/testData/klibABI/removeAbstractCallableFromAbstractClassOrInterface/"); + } + + @TestMetadata("removeCallable") + public void testRemoveCallable() throws Exception { + runTest("compiler/testData/klibABI/removeCallable/"); + } + + @TestMetadata("removeClass") + public void testRemoveClass() throws Exception { + runTest("compiler/testData/klibABI/removeClass/"); + } + + @TestMetadata("removeEnumEntry") + public void testRemoveEnumEntry() throws Exception { + runTest("compiler/testData/klibABI/removeEnumEntry/"); + } + + @TestMetadata("removeSealedSubclass") + public void testRemoveSealedSubclass() throws Exception { + runTest("compiler/testData/klibABI/removeSealedSubclass/"); + } + + @TestMetadata("replaceCallableReturnType") + public void testReplaceCallableReturnType() throws Exception { + runTest("compiler/testData/klibABI/replaceCallableReturnType/"); + } + + @TestMetadata("typeAliasChanges") + public void testTypeAliasChanges() throws Exception { + runTest("compiler/testData/klibABI/typeAliasChanges/"); + } +}