[PL][tests] K/JS: Run compiler through CLI interface

This commit is contained in:
Dmitriy Dolovov
2023-07-11 16:58:12 +02:00
committed by Space Team
parent 19c6208cc0
commit 715aba3d18
2 changed files with 128 additions and 279 deletions
@@ -21,7 +21,7 @@ object PartialLinkageTestUtils {
val testModeConstructorParameters: Map<String, String> val testModeConstructorParameters: Map<String, String>
// Customize the source code of a module before compiling it to a KLIB. // 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. // Build a KLIB from a module.
fun buildKlib(moduleName: String, buildDirs: ModuleBuildDirs, dependencies: Dependencies, klibFile: File) fun buildKlib(moduleName: String, buildDirs: ModuleBuildDirs, dependencies: Dependencies, klibFile: File)
@@ -93,6 +93,9 @@ object PartialLinkageTestUtils {
// Populate the source dir with *.kt files. // Populate the source dir with *.kt files.
copySources(from = moduleTestDir, to = moduleBuildDirs.sourceDir) copySources(from = moduleTestDir, to = moduleBuildDirs.sourceDir)
// Customize the source dir if necessary.
customizeModuleSources(moduleName, moduleBuildDirs.sourceDir)
// Include PL utils into the main module. // Include PL utils into the main module.
if (moduleName == MAIN_MODULE_NAME) { if (moduleName == MAIN_MODULE_NAME) {
val utilsDir = testDir.parentFile.resolve(PL_UTILS_DIR) val utilsDir = testDir.parentFile.resolve(PL_UTILS_DIR)
@@ -110,8 +113,6 @@ object PartialLinkageTestUtils {
} }
} }
customizeModuleSources(moduleName, moduleBuildDirs.sourceDir)
moduleBuildDirs.outputDir.apply { mkdirs() } moduleBuildDirs.outputDir.apply { mkdirs() }
this[moduleName] = ModuleUnderTest( this[moduleName] = ModuleUnderTest(
@@ -5,54 +5,20 @@
package org.jetbrains.kotlin.js.test.ir 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 com.intellij.testFramework.TestDataFile
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.js.K2JsIrCompiler
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.js.klib.* 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.*
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.js.testOld.V8IrJsTestChecker
import org.jetbrains.kotlin.klib.PartialLinkageTestUtils import org.jetbrains.kotlin.klib.PartialLinkageTestUtils
import org.jetbrains.kotlin.klib.PartialLinkageTestUtils.Dependencies import org.jetbrains.kotlin.klib.PartialLinkageTestUtils.Dependencies
import org.jetbrains.kotlin.klib.PartialLinkageTestUtils.MAIN_MODULE_NAME import org.jetbrains.kotlin.klib.PartialLinkageTestUtils.MAIN_MODULE_NAME
import org.jetbrains.kotlin.klib.PartialLinkageTestUtils.ModuleBuildDirs 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 org.junit.jupiter.api.AfterEach
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.PrintStream import java.io.PrintStream
import java.nio.charset.Charset
import kotlin.io.path.createTempDirectory import kotlin.io.path.createTempDirectory
abstract class AbstractJsPartialLinkageNoICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K1_NO_IC) 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 AbstractJsPartialLinkageWithICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K1_WITH_IC)
abstract class AbstractFirJsPartialLinkageNoICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K2_NO_IC) abstract class AbstractFirJsPartialLinkageNoICTestCase : AbstractJsPartialLinkageTestCase(CompilerType.K2_NO_IC)
abstract class AbstractJsPartialLinkageTestCase(val compilerType: CompilerType) { abstract class AbstractJsPartialLinkageTestCase(private val compilerType: CompilerType) {
enum class CompilerType(val es6Mode: Boolean) { enum class CompilerType(val useFir: Boolean, val es6Mode: Boolean, val useIc: Boolean) {
K1_NO_IC(false), K1_NO_IC(useFir = false, es6Mode = false, useIc = false),
K1_NO_IC_WITH_ES6(true), K1_NO_IC_WITH_ES6(useFir = false, es6Mode = true, useIc = false),
K1_WITH_IC(false), K1_WITH_IC(useFir = false, es6Mode = false, useIc = true),
K2_NO_IC(false) K2_NO_IC(useFir = true, es6Mode = false, useIc = false)
} }
private val zipAccessor = ZipFileSystemCacheableAccessor(2) private val buildDir: File = createTempDirectory().toFile().also { it.mkdirs() }
private val buildDir = createTempDirectory().toFile().also { it.mkdirs() }
private val environment =
KotlinCoreEnvironment.createForParallelTests(TestDisposable(), CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES)
@AfterEach @AfterEach
fun clearArtifacts() { fun clearArtifacts() {
zipAccessor.reset()
buildDir.deleteRecursively() 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 { private inner class JsTestConfiguration(testPath: String) : PartialLinkageTestUtils.TestConfiguration {
override val testDir: File = File(testPath).absoluteFile override val testDir: File = File(testPath).absoluteFile
override val buildDir: File get() = this@AbstractJsPartialLinkageTestCase.buildDir 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 stdlibFile: File get() = File("libraries/stdlib/js-ir/build/classes/kotlin/js/main").absoluteFile
override val testModeConstructorParameters = mapOf("isJs" to "true") override val testModeConstructorParameters = mapOf("isJs" to "true")
override fun customizeModuleSources(moduleName: String, moduleSourceDir: File) {
if (moduleName == MAIN_MODULE_NAME)
customizeMainModuleSources(moduleSourceDir)
}
override fun buildKlib( override fun buildKlib(
moduleName: String, moduleName: String,
buildDirs: ModuleBuildDirs, buildDirs: ModuleBuildDirs,
dependencies: Dependencies, dependencies: Dependencies,
klibFile: File klibFile: File,
) = this@AbstractJsPartialLinkageTestCase.buildKlib(moduleName, buildDirs, dependencies, klibFile) ) = this@AbstractJsPartialLinkageTestCase.buildKlib(moduleName, buildDirs, dependencies, klibFile)
override fun buildBinaryAndRun(mainModuleKlibFile: File, dependencies: Dependencies) = override fun buildBinaryAndRun(mainModuleKlibFile: File, dependencies: Dependencies) =
this@AbstractJsPartialLinkageTestCase.buildBinaryAndRun(mainModuleKlibFile, dependencies) this@AbstractJsPartialLinkageTestCase.buildBinaryAndRun(mainModuleKlibFile, dependencies)
override fun onNonEmptyBuildDirectory(directory: File) { override fun onNonEmptyBuildDirectory(directory: File) {
zipAccessor.reset()
directory.listFiles()?.forEach(File::deleteRecursively) directory.listFiles()?.forEach(File::deleteRecursively)
} }
@@ -121,260 +75,154 @@ abstract class AbstractJsPartialLinkageTestCase(val compilerType: CompilerType)
// The entry point to generated test classes. // The entry point to generated test classes.
fun runTest(@TestDataFile testPath: String) = PartialLinkageTestUtils.runTest(JsTestConfiguration(testPath)) fun runTest(@TestDataFile testPath: String) = PartialLinkageTestUtils.runTest(JsTestConfiguration(testPath))
private fun buildKlib(moduleName: String, buildDirs: ModuleBuildDirs, dependencies: Dependencies, klibFile: File) { private fun customizeMainModuleSources(moduleSourceDir: File) {
buildDirs.sourceDir.walkTopDown() // Add @JsExport annotation to make box function be visible for Node.
.filter { file -> file.isFile && file.extension == "js" } moduleSourceDir.walkTopDown().forEach { file ->
.forEach { file -> file.copyTo(buildDirs.outputDir.resolve(file.relativeTo(buildDirs.sourceDir)), overwrite = true) } if (file.extension == "kt") {
var modified = false
when (compilerType) { val lines = file.readLines().map { line ->
CompilerType.K1_NO_IC, CompilerType.K1_NO_IC_WITH_ES6, CompilerType.K1_WITH_IC -> if (line.startsWith("fun $BOX_FUN_FQN()")) {
buildKlibWithK1(moduleName, buildDirs.sourceDir, dependencies, klibFile) modified = true
CompilerType.K2_NO_IC -> buildKlibWithK2(moduleName, buildDirs.sourceDir, dependencies, klibFile) "@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) { fun buildKlib(moduleName: String, buildDirs: ModuleBuildDirs, dependencies: Dependencies, klibFile: File) {
val config = createConfig(moduleName) val kotlinSourceFilePaths = mutableListOf<String>()
val ktFiles = environment.createPsiFiles(moduleSourceDir)
val regularDependencies = dependencies.regularDependencies.map { it.libraryFile.absolutePath } buildDirs.sourceDir.walkTopDown().forEach { sourceFile ->
val friendDependencies = dependencies.friendDependencies.map { it.libraryFile.absolutePath } if (sourceFile.isFile) when (sourceFile.extension) {
"kt" -> kotlinSourceFilePaths += sourceFile.absolutePath
val moduleStructure = prepareAnalyzedSourceModule( "js" -> {
environment.project, // This is needed to preserve *.js files from test data which are required for tests with `external` declarations:
ktFiles, sourceFile.copyTo(buildDirs.outputDir.resolve(sourceFile.relativeTo(buildDirs.sourceDir)), overwrite = true)
config, }
regularDependencies, }
friendDependencies,
AnalyzerWithCompilerReport(config)
)
val moduleSourceFiles = (moduleStructure.mainModule as MainModule.SourceFiles).files
val icData = moduleStructure.compilerConfiguration.incrementalDataProvider?.getSerializedData(moduleSourceFiles) ?: emptyList()
val expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
val (moduleFragment, _) = generateIrForKlibSerialization(
environment.project,
moduleSourceFiles,
config,
moduleStructure.jsFrontEndResult.jsAnalysisResult,
sortDependencies(moduleStructure.moduleDependencies),
icData,
expectDescriptorToSymbol,
IrFactoryImpl,
verifySignatures = true
) {
moduleStructure.getModuleDescriptor(it)
} }
val metadataSerializer = // Build KLIB:
KlibMetadataIncrementalSerializer(config, moduleStructure.project, moduleStructure.jsFrontEndResult.hasErrors) runCompilerViaCLI(
listOf(
generateKLib( "-Xir-produce-klib-file",
moduleStructure, "-ir-output-dir", klibFile.parentFile.absolutePath,
klibFile.path, "-ir-output-name", moduleName
nopack = false, ),
jsOutputName = moduleName, dependencies.toCompilerArgs(),
icData = icData, listOf("-language-version", "2.0").takeIf { compilerType.useFir },
expectDescriptorToSymbol = expectDescriptorToSymbol, kotlinSourceFilePaths
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
) )
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) { private fun buildBinaryAndRun(mainModuleKlibFile: File, allDependencies: Dependencies) {
val configuration = createConfig(MAIN_MODULE_NAME) // The modules in `Dependencies.regularDependencies` are already in topological order.
configuration.setupPartialLinkageConfig(PartialLinkageConfig(PartialLinkageMode.ENABLE, PartialLinkageLogLevel.WARNING)) // It is important to pass the provided and the produced JS files to Node in exactly the same order.
val knownModulesInTopologicalOrder: List<ModuleDetails> = allDependencies.regularDependencies.map { dependency ->
val compilationOutputs = when (compilerType) { ModuleDetails(name = dependency.moduleName, outputDir = dependency.libraryFile.parentFile)
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)
} }
val knownModuleNames: Set<ModuleName> = 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 runCompilerViaCLI(
val producedBinaries: Map<String, List<File>> = compilationOutputs listOf(
.writeAll(binariesDir, MAIN_MODULE_NAME, false, MAIN_MODULE_NAME, ModuleKind.PLAIN) "-Xir-produce-js",
.filter { file -> file.extension == "js" } "-Xir-per-module",
.groupByTo(linkedMapOf()) { file -> file.nameWithoutExtension.toInnerName() } "-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 // All JS files produced during the compiler call.
val providedBinaries: Map<String, List<File>> = val producedBinaries: Map<ModuleName, File> = binariesDir.walkTopDown()
(allDependencies.regularDependencies.asSequence().map { it.libraryFile } + mainModuleKlibFile) .filter { binaryFile -> binaryFile.extension == "js" }
.mapNotNull { klibFile -> .associateBy { binaryFile -> binaryFile.guessModuleName() }
val moduleName = if (klibFile.extension == "klib") klibFile.nameWithoutExtension else return@mapNotNull null
val outputDir = klibFile.parentFile
val providedJsFiles = outputDir.listFiles()?.filter { it.isFile && it.extension == "js" }.orEmpty() val unexpectedModuleNames = producedBinaries.keys - knownModuleNames
if (providedJsFiles.isEmpty()) return@mapNotNull null
moduleName to providedJsFiles
}.toMap()
val unexpectedModuleNames = providedBinaries.keys - producedBinaries.keys
check(unexpectedModuleNames.isEmpty()) { "Unexpected module names: $unexpectedModuleNames" } check(unexpectedModuleNames.isEmpty()) { "Unexpected module names: $unexpectedModuleNames" }
val allBinaries: List<File> = buildList { val allBinaries: List<File> = buildList {
producedBinaries.forEach { (moduleName, producedJsFiles) -> knownModulesInTopologicalOrder.forEach { moduleDetails ->
this += providedBinaries[moduleName].orEmpty() // A set of JS files directly out of test data (aka "external" JS files) to be used immediately in the application:
this += producedJsFiles val providedBinaries: List<File> = 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) executeAndCheckBinaries(MAIN_MODULE_NAME, allBinaries)
} }
private fun buildBinaryNoIC( private fun File.guessModuleName(): String {
configuration: CompilerConfiguration, return when {
mainModuleKlibFile: File, extension != "js" -> error("Not a JS file: $this")
allDependencies: Dependencies, name.startsWith("kotlin-") && "stdlib" in name -> "stdlib"
es6mode: Boolean else -> nameWithoutExtension.removePrefix("kotlin_")
): 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 buildBinaryWithIC( private fun Dependencies.toCompilerArgs(): List<String> = buildList {
configuration: CompilerConfiguration, if (regularDependencies.isNotEmpty()) {
mainModuleKlibFile: File, this += "-libraries"
allDependencies: Dependencies this += regularDependencies.joinToString(File.pathSeparator) { it.libraryFile.absolutePath }
): CompilationOutputs { }
// TODO: what about friend dependencies? if (friendDependencies.isNotEmpty()) {
val cacheUpdater = CacheUpdater( this += "-Xfriend-modules=${friendDependencies.joinToString(File.pathSeparator) { it.libraryFile.absolutePath }}"
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 val IrModuleFragment.exportName get() = "kotlin_${name.asStringStripSpecialMarkers()}" private fun runCompilerViaCLI(vararg compilerArgs: List<String?>?) {
private fun String.toInnerName() = if (startsWith("kotlin_")) substringAfter("kotlin_") else this val allCompilerArgs = compilerArgs.flatMap { args -> args.orEmpty().filterNotNull() }.toTypedArray()
private fun KotlinCoreEnvironment.createPsiFiles(sourceDir: File): List<KtFile> { val compilerXmlOutput = ByteArrayOutputStream()
val psiManager = PsiManager.getInstance(project) val exitCode = PrintStream(compilerXmlOutput).use { printStream ->
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem K2JsIrCompiler().execFullPathsInMessages(printStream, allCompilerArgs)
}
return sourceDir.walkTopDown().filter { file -> if (exitCode != ExitCode.OK)
file.isFile && file.extension == "kt" throw AssertionError(
}.flatMap { file -> buildString {
val virtualFile = fileSystem.findFileByIoFile(file) ?: error("VirtualFile for $file not found") appendLine("Compiler failure.")
SingleRootFileViewProvider(psiManager, virtualFile).allFiles appendLine("Exit code = $exitCode.")
}.filterIsInstance<KtFile>().toList() 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<File>) { private fun executeAndCheckBinaries(mainModuleName: String, dependencies: Collection<File>) {
val checker = V8IrJsTestChecker val checker = V8IrJsTestChecker
val filePaths = dependencies.map { it.canonicalPath } 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 { companion object {
private const val BIN_DIR_NAME = "_bins_js" 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)