From 715aba3d18e6905f265cc15849229b8d91eec668 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Tue, 11 Jul 2023 16:58:12 +0200 Subject: [PATCH] [PL][tests] K/JS: Run compiler through CLI interface --- .../kotlin/klib/PartialLinkageTestUtils.kt | 7 +- .../ir/AbstractJsPartialLinkageTestCase.kt | 400 ++++++------------ 2 files changed, 128 insertions(+), 279 deletions(-) diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/klib/PartialLinkageTestUtils.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/klib/PartialLinkageTestUtils.kt index 5c4a7d669d6..1c13df628fe 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/klib/PartialLinkageTestUtils.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/klib/PartialLinkageTestUtils.kt @@ -21,7 +21,7 @@ object PartialLinkageTestUtils { val testModeConstructorParameters: Map // Customize the source code of a module before compiling it to a KLIB. - fun customizeModuleSources(moduleName: String, moduleSourceDir: File) = Unit + fun customizeModuleSources(moduleName: String, moduleSourceDir: File) // Build a KLIB from a module. fun buildKlib(moduleName: String, buildDirs: ModuleBuildDirs, dependencies: Dependencies, klibFile: File) @@ -93,6 +93,9 @@ object PartialLinkageTestUtils { // Populate the source dir with *.kt files. copySources(from = moduleTestDir, to = moduleBuildDirs.sourceDir) + // Customize the source dir if necessary. + customizeModuleSources(moduleName, moduleBuildDirs.sourceDir) + // Include PL utils into the main module. if (moduleName == MAIN_MODULE_NAME) { val utilsDir = testDir.parentFile.resolve(PL_UTILS_DIR) @@ -110,8 +113,6 @@ object PartialLinkageTestUtils { } } - customizeModuleSources(moduleName, moduleBuildDirs.sourceDir) - moduleBuildDirs.outputDir.apply { mkdirs() } this[moduleName] = ModuleUnderTest( 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 d1fe65253bd..76f45f33453 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 @@ -5,54 +5,20 @@ package org.jetbrains.kotlin.js.test.ir -import com.intellij.openapi.vfs.StandardFileSystems -import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.openapi.vfs.local.CoreLocalFileSystem -import com.intellij.psi.PsiManager -import com.intellij.psi.SingleRootFileViewProvider import com.intellij.testFramework.TestDataFile -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.common.ExitCode +import org.jetbrains.kotlin.cli.js.K2JsIrCompiler import org.jetbrains.kotlin.cli.js.klib.* -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.ic.CacheUpdater -import org.jetbrains.kotlin.ir.backend.js.ic.JsExecutableProducer -import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs -import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer -import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity -import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl -import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC -import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageConfig -import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageLogLevel -import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageMode -import org.jetbrains.kotlin.ir.linkage.partial.setupPartialLinkageConfig -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.klib.PartialLinkageTestUtils.ModuleBuildDirs -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.utils.TestDisposable import org.junit.jupiter.api.AfterEach import java.io.ByteArrayOutputStream import java.io.File import java.io.PrintStream -import java.nio.charset.Charset import kotlin.io.path.createTempDirectory abstract class AbstractJsPartialLinkageNoICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K1_NO_IC) @@ -60,56 +26,44 @@ abstract class AbstractJsPartialLinkageNoICES6TestCase : AbstractJsPartialLinkag abstract class AbstractJsPartialLinkageWithICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K1_WITH_IC) abstract class AbstractFirJsPartialLinkageNoICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K2_NO_IC) -abstract class AbstractJsPartialLinkageTestCase(val compilerType: CompilerType) { - enum class CompilerType(val es6Mode: Boolean) { - K1_NO_IC(false), - K1_NO_IC_WITH_ES6(true), - K1_WITH_IC(false), - K2_NO_IC(false) +abstract class AbstractJsPartialLinkageTestCase(private val compilerType: CompilerType) { + enum class CompilerType(val useFir: Boolean, val es6Mode: Boolean, val useIc: Boolean) { + K1_NO_IC(useFir = false, es6Mode = false, useIc = false), + K1_NO_IC_WITH_ES6(useFir = false, es6Mode = true, useIc = false), + K1_WITH_IC(useFir = false, es6Mode = false, useIc = true), + K2_NO_IC(useFir = true, es6Mode = false, useIc = false) } - private val zipAccessor = ZipFileSystemCacheableAccessor(2) - private val buildDir = createTempDirectory().toFile().also { it.mkdirs() } - private val environment = - KotlinCoreEnvironment.createForParallelTests(TestDisposable(), CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES) + private val buildDir: File = createTempDirectory().toFile().also { it.mkdirs() } @AfterEach fun clearArtifacts() { - zipAccessor.reset() buildDir.deleteRecursively() } - private fun createConfig(moduleName: String): CompilerConfiguration { - val config = environment.configuration.copy() - config.put(CommonConfigurationKeys.MODULE_NAME, moduleName) - config.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN) - config.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true) - - zipAccessor.reset() - config.put(JSConfigurationKeys.ZIP_FILE_SYSTEM_ACCESSOR, zipAccessor) - - return config - } - private inner class JsTestConfiguration(testPath: String) : PartialLinkageTestUtils.TestConfiguration { 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 testModeConstructorParameters = mapOf("isJs" to "true") + override fun customizeModuleSources(moduleName: String, moduleSourceDir: File) { + if (moduleName == MAIN_MODULE_NAME) + customizeMainModuleSources(moduleSourceDir) + } + override fun buildKlib( moduleName: String, buildDirs: ModuleBuildDirs, dependencies: Dependencies, - klibFile: File + klibFile: File, ) = this@AbstractJsPartialLinkageTestCase.buildKlib(moduleName, buildDirs, dependencies, klibFile) override fun buildBinaryAndRun(mainModuleKlibFile: File, dependencies: Dependencies) = this@AbstractJsPartialLinkageTestCase.buildBinaryAndRun(mainModuleKlibFile, dependencies) override fun onNonEmptyBuildDirectory(directory: File) { - zipAccessor.reset() directory.listFiles()?.forEach(File::deleteRecursively) } @@ -121,260 +75,154 @@ abstract class AbstractJsPartialLinkageTestCase(val compilerType: CompilerType) // The entry point to generated test classes. fun runTest(@TestDataFile testPath: String) = PartialLinkageTestUtils.runTest(JsTestConfiguration(testPath)) - private fun buildKlib(moduleName: String, buildDirs: ModuleBuildDirs, dependencies: Dependencies, klibFile: File) { - buildDirs.sourceDir.walkTopDown() - .filter { file -> file.isFile && file.extension == "js" } - .forEach { file -> file.copyTo(buildDirs.outputDir.resolve(file.relativeTo(buildDirs.sourceDir)), overwrite = true) } - - when (compilerType) { - CompilerType.K1_NO_IC, CompilerType.K1_NO_IC_WITH_ES6, CompilerType.K1_WITH_IC -> - buildKlibWithK1(moduleName, buildDirs.sourceDir, dependencies, klibFile) - CompilerType.K2_NO_IC -> buildKlibWithK2(moduleName, buildDirs.sourceDir, dependencies, klibFile) + private fun customizeMainModuleSources(moduleSourceDir: File) { + // Add @JsExport annotation to make box function be visible for Node. + moduleSourceDir.walkTopDown().forEach { file -> + if (file.extension == "kt") { + var modified = false + val lines = file.readLines().map { line -> + if (line.startsWith("fun $BOX_FUN_FQN()")) { + modified = true + "@OptIn(ExperimentalJsExport::class) @JsExport $line" + } else + line + } + if (modified) file.writeText(lines.joinToString("\n")) + } } } - private fun buildKlibWithK1(moduleName: String, moduleSourceDir: File, dependencies: Dependencies, klibFile: File) { - val config = createConfig(moduleName) - val ktFiles = environment.createPsiFiles(moduleSourceDir) + fun buildKlib(moduleName: String, buildDirs: ModuleBuildDirs, dependencies: Dependencies, klibFile: File) { + val kotlinSourceFilePaths = mutableListOf() - val regularDependencies = dependencies.regularDependencies.map { it.libraryFile.absolutePath } - val friendDependencies = dependencies.friendDependencies.map { it.libraryFile.absolutePath } - - val moduleStructure = prepareAnalyzedSourceModule( - environment.project, - ktFiles, - config, - regularDependencies, - friendDependencies, - AnalyzerWithCompilerReport(config) - ) - - 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, - moduleStructure.jsFrontEndResult.jsAnalysisResult, - sortDependencies(moduleStructure.moduleDependencies), - icData, - expectDescriptorToSymbol, - IrFactoryImpl, - verifySignatures = true - ) { - moduleStructure.getModuleDescriptor(it) + buildDirs.sourceDir.walkTopDown().forEach { sourceFile -> + if (sourceFile.isFile) when (sourceFile.extension) { + "kt" -> kotlinSourceFilePaths += sourceFile.absolutePath + "js" -> { + // This is needed to preserve *.js files from test data which are required for tests with `external` declarations: + sourceFile.copyTo(buildDirs.outputDir.resolve(sourceFile.relativeTo(buildDirs.sourceDir)), overwrite = true) + } + } } - val metadataSerializer = - KlibMetadataIncrementalSerializer(config, moduleStructure.project, moduleStructure.jsFrontEndResult.hasErrors) - - generateKLib( - moduleStructure, - klibFile.path, - nopack = false, - jsOutputName = moduleName, - icData = icData, - expectDescriptorToSymbol = expectDescriptorToSymbol, - moduleFragment = moduleFragment - ) { file -> - metadataSerializer.serializeScope(file, moduleStructure.jsFrontEndResult.bindingContext, moduleFragment.descriptor) - } - } - - 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 + // Build KLIB: + runCompilerViaCLI( + listOf( + "-Xir-produce-klib-file", + "-ir-output-dir", klibFile.parentFile.absolutePath, + "-ir-output-name", moduleName + ), + dependencies.toCompilerArgs(), + listOf("-language-version", "2.0").takeIf { compilerType.useFir }, + kotlinSourceFilePaths ) - - val outputStream = ByteArrayOutputStream() - val messageCollector = PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.PLAIN_FULL_PATHS, true) - - val analyzedOutput = compileModuleToAnalyzedFirWithPsi( - moduleStructure = moduleStructure, - ktFiles = ktFiles, - libraries = regularDependencies, - friendLibraries = friendDependencies, - diagnosticsReporter = diagnosticsReporter, - incrementalDataProvider = null, - lookupTracker = null - ) - - val fir2IrActualizedResult = transformFirToIr(moduleStructure, analyzedOutput.output, diagnosticsReporter) - - if (analyzedOutput.reportCompilationErrors(moduleStructure, diagnosticsReporter, messageCollector)) { - val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8")) - throw AssertionError("The following errors occurred compiling test:\n$messages") - } - - serializeFirKlib( - moduleStructure = moduleStructure, - firOutputs = analyzedOutput.output, - 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 serializing test klib:\n$messages") - } } private fun buildBinaryAndRun(mainModuleKlibFile: File, allDependencies: Dependencies) { - val configuration = createConfig(MAIN_MODULE_NAME) - configuration.setupPartialLinkageConfig(PartialLinkageConfig(PartialLinkageMode.ENABLE, PartialLinkageLogLevel.WARNING)) - - val compilationOutputs = when (compilerType) { - CompilerType.K1_NO_IC, CompilerType.K1_NO_IC_WITH_ES6, CompilerType.K2_NO_IC -> - buildBinaryNoIC(configuration, mainModuleKlibFile, allDependencies, compilerType.es6Mode) - CompilerType.K1_WITH_IC -> buildBinaryWithIC(configuration, mainModuleKlibFile, allDependencies) + // The modules in `Dependencies.regularDependencies` are already in topological order. + // It is important to pass the provided and the produced JS files to Node in exactly the same order. + val knownModulesInTopologicalOrder: List = allDependencies.regularDependencies.map { dependency -> + ModuleDetails(name = dependency.moduleName, outputDir = dependency.libraryFile.parentFile) } + val knownModuleNames: Set = knownModulesInTopologicalOrder.mapTo(hashSetOf(), ModuleDetails::name) - val binariesDir = File(buildDir, BIN_DIR_NAME).also { it.mkdirs() } + val binariesDir: File = File(buildDir, BIN_DIR_NAME).also { it.mkdirs() } - // key = module name, value = list of produced JS files - val producedBinaries: Map> = compilationOutputs - .writeAll(binariesDir, MAIN_MODULE_NAME, false, MAIN_MODULE_NAME, ModuleKind.PLAIN) - .filter { file -> file.extension == "js" } - .groupByTo(linkedMapOf()) { file -> file.nameWithoutExtension.toInnerName() } + runCompilerViaCLI( + listOf( + "-Xir-produce-js", + "-Xir-per-module", + "-module-kind", "plain", + "-Xinclude=${mainModuleKlibFile.absolutePath}", + "-ir-output-dir", binariesDir.absolutePath, + "-ir-output-name", MAIN_MODULE_NAME + ), + listOf( + "-Xcache-directory", + buildDir.resolve("libs-cache").absolutePath + ).takeIf { compilerType.useIc }, + allDependencies.toCompilerArgs(), + listOf("-Xes-classes").takeIf { compilerType.es6Mode }, + listOf( + "-Xpartial-linkage=enable", + "-Xpartial-linkage-loglevel=WARNING" + ) + ) - // key = module name, value = list of JS files out of test data - val providedBinaries: Map> = - (allDependencies.regularDependencies.asSequence().map { it.libraryFile } + mainModuleKlibFile) - .mapNotNull { klibFile -> - val moduleName = if (klibFile.extension == "klib") klibFile.nameWithoutExtension else return@mapNotNull null - val outputDir = klibFile.parentFile + // All JS files produced during the compiler call. + val producedBinaries: Map = binariesDir.walkTopDown() + .filter { binaryFile -> binaryFile.extension == "js" } + .associateBy { binaryFile -> binaryFile.guessModuleName() } - val providedJsFiles = outputDir.listFiles()?.filter { it.isFile && it.extension == "js" }.orEmpty() - if (providedJsFiles.isEmpty()) return@mapNotNull null - - moduleName to providedJsFiles - }.toMap() - - val unexpectedModuleNames = providedBinaries.keys - producedBinaries.keys + val unexpectedModuleNames = producedBinaries.keys - knownModuleNames check(unexpectedModuleNames.isEmpty()) { "Unexpected module names: $unexpectedModuleNames" } val allBinaries: List = buildList { - producedBinaries.forEach { (moduleName, producedJsFiles) -> - this += providedBinaries[moduleName].orEmpty() - this += producedJsFiles + knownModulesInTopologicalOrder.forEach { moduleDetails -> + // A set of JS files directly out of test data (aka "external" JS files) to be used immediately in the application: + val providedBinaries: List = moduleDetails.outputDir.listFiles()?.filter { it.extension == "js" }.orEmpty() + addAll(providedBinaries) + + // A JS file produced by the compiler for the given module: + val producedBinary: File? = producedBinaries[moduleDetails.name] + producedBinary?.let(::add) } } executeAndCheckBinaries(MAIN_MODULE_NAME, allBinaries) } - private fun buildBinaryNoIC( - configuration: CompilerConfiguration, - mainModuleKlibFile: File, - allDependencies: Dependencies, - es6mode: Boolean - ): CompilationOutputs { - val klib = MainModule.Klib(mainModuleKlibFile.path) - val moduleStructure = ModulesStructure( - environment.project, - klib, - configuration, - allDependencies.regularDependencies.map { it.libraryFile.path }, - allDependencies.friendDependencies.map { it.libraryFile.path } - ) - - val ir = compile( - moduleStructure, - PhaseConfig(jsPhases), - IrFactoryImplForJsIC(WholeWorldStageController()), - exportedDeclarations = setOf(BOX_FUN_FQN), - granularity = JsGenerationGranularity.PER_MODULE, - es6mode = es6mode - ) - - val transformer = IrModuleToJsTransformer( - backendContext = ir.context, - mainArguments = emptyList() - ) - - val compiledResult = transformer.generateModule( - modules = ir.allModules, - modes = setOf(TranslationMode.PER_MODULE_DEV), - relativeRequirePath = false - ) - - return compiledResult.outputs[TranslationMode.PER_MODULE_DEV] ?: error("No compiler output") + private fun File.guessModuleName(): String { + return when { + extension != "js" -> error("Not a JS file: $this") + name.startsWith("kotlin-") && "stdlib" in name -> "stdlib" + else -> nameWithoutExtension.removePrefix("kotlin_") + } } - 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(granularity = JsGenerationGranularity.PER_MODULE, outJsProgram = true).compilationOut + private fun Dependencies.toCompilerArgs(): List = buildList { + if (regularDependencies.isNotEmpty()) { + this += "-libraries" + this += regularDependencies.joinToString(File.pathSeparator) { it.libraryFile.absolutePath } + } + if (friendDependencies.isNotEmpty()) { + this += "-Xfriend-modules=${friendDependencies.joinToString(File.pathSeparator) { it.libraryFile.absolutePath }}" + } } - private val IrModuleFragment.exportName get() = "kotlin_${name.asStringStripSpecialMarkers()}" - private fun String.toInnerName() = if (startsWith("kotlin_")) substringAfter("kotlin_") else this + private fun runCompilerViaCLI(vararg compilerArgs: List?) { + val allCompilerArgs = compilerArgs.flatMap { args -> args.orEmpty().filterNotNull() }.toTypedArray() - private fun KotlinCoreEnvironment.createPsiFiles(sourceDir: File): List { - val psiManager = PsiManager.getInstance(project) - val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem + val compilerXmlOutput = ByteArrayOutputStream() + val exitCode = PrintStream(compilerXmlOutput).use { printStream -> + K2JsIrCompiler().execFullPathsInMessages(printStream, allCompilerArgs) + } - return sourceDir.walkTopDown().filter { file -> - file.isFile && file.extension == "kt" - }.flatMap { file -> - val virtualFile = fileSystem.findFileByIoFile(file) ?: error("VirtualFile for $file not found") - SingleRootFileViewProvider(psiManager, virtualFile).allFiles - }.filterIsInstance().toList() + if (exitCode != ExitCode.OK) + throw AssertionError( + buildString { + appendLine("Compiler failure.") + appendLine("Exit code = $exitCode.") + appendLine("Compiler messages:") + appendLine("==========") + appendLine(compilerXmlOutput.toString(Charsets.UTF_8.name())) + appendLine("==========") + } + ) } - private fun File.binJsFile(name: String): File = File(this, "$name.js") - private fun executeAndCheckBinaries(mainModuleName: String, dependencies: Collection) { val checker = V8IrJsTestChecker val filePaths = dependencies.map { it.canonicalPath } - checker.check(filePaths, mainModuleName, null, BOX_FUN_FQN.asString(), "OK", withModuleSystem = false) + checker.check(filePaths, mainModuleName, null, BOX_FUN_FQN, "OK", withModuleSystem = false) } companion object { private const val BIN_DIR_NAME = "_bins_js" - private val BOX_FUN_FQN = FqName("box") + private const val BOX_FUN_FQN = "box" } } + +private typealias ModuleName = String +private class ModuleDetails(val name: ModuleName, val outputDir: File)