diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index f3e0b5a04e0..ff344853ad6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -28,12 +28,14 @@ import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMet import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataSerializationUtil import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataVersion import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.createJsKlibMetadataPackageFragmentProvider +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.* +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.* import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns -import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator +import org.jetbrains.kotlin.ir.util.SymbolTable +import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.psi.KtFile @@ -47,19 +49,20 @@ import java.nio.file.Files.move import java.nio.file.Paths import java.nio.file.StandardCopyOption -class CompiledModule( +sealed class TranslationResult { + class CompiledJsCode(val jsCode: String) : TranslationResult() + + object CompiledKlib : TranslationResult() +} + +data class KlibModuleRef( val moduleName: String, - val generatedCode: String?, - var moduleDescriptor: ModuleDescriptorImpl?, - val klibPath: String, - val dependencies: List, - val isBuiltIn: Boolean + val klibPath: String ) -enum class CompilationMode(val generateJS: Boolean, val generateKlib: Boolean) { - KLIB(false, true), - KLIB_WITH_JS(true, true), - JS_AGAINST_KLIB(true, false) +enum class CompilationMode { + KLIB, + JS } private val moduleHeaderFileName = "module.kji" @@ -74,199 +77,150 @@ private val logggg = object : LoggingContext { private fun metadataFileName(moduleName: String) = "$moduleName.${JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION}" - -data class JsKlib( - val moduleDescriptor: ModuleDescriptorImpl, - val moduleIr: IrModuleFragment, - val symbolTable: SymbolTable, - val irBuiltIns: IrBuiltIns, - val deserializer: JsIrLinker -) - - -private fun deserializeModuleFromKlib( - locationDir: String, - moduleName: String, - lookupTracker: LookupTracker, - storageManager: LockBasedStorageManager, - metadataVersion: JsKlibMetadataVersion, - languageVersionSettings: LanguageVersionSettings, - dependencies: List, - builtinsModule: ModuleDescriptorImpl? -): JsKlib { - val klibDirFile = File(locationDir) - val md = loadKlibMetadata( - moduleName, - locationDir, - builtinsModule == null, - lookupTracker, - storageManager, - metadataVersion, - languageVersionSettings, - builtinsModule, - dependencies - ) - - val st = SymbolTable() - val typeTranslator = TypeTranslator(st, languageVersionSettings).also { - it.constantValueGenerator = ConstantValueGenerator(md, st) - } - - val irBuiltIns = IrBuiltIns(md.builtIns, typeTranslator, st) - - val moduleFile = File(klibDirFile, moduleHeaderFileName) - val deserializer = JsIrLinker(md, logggg, irBuiltIns, st) - - dependencies.forEach { - val dependencyKlibDir = File(it.klibPath, moduleHeaderFileName) - deserializer.deserializeIrModuleHeader(it.moduleDescriptor!!, dependencyKlibDir.readBytes(), File(it.klibPath), DeserializationStrategy.ONLY_REFERENCED) - } - - val moduleFragment = deserializer.deserializeIrModuleHeader(md, moduleFile.readBytes(), klibDirFile, DeserializationStrategy.ALL) - - return JsKlib(md, moduleFragment, st, irBuiltIns, deserializer) -} +private val CompilerConfiguration.metadataVersion + get() = get(CommonConfigurationKeys.METADATA_VERSION) as? JsKlibMetadataVersion ?: JsKlibMetadataVersion.INSTANCE fun compile( project: Project, files: List, configuration: CompilerConfiguration, - export: List = emptyList(), compileMode: CompilationMode, - dependencies: List = emptyList(), - klibPath: String -): CompiledModule { - val metadataVersion = configuration.get(CommonConfigurationKeys.METADATA_VERSION) as? JsKlibMetadataVersion - ?: JsKlibMetadataVersion.INSTANCE - val lookupTracker = LookupTracker.DO_NOTHING - val languageSettings = configuration.languageVersionSettings - val storageManager = LockBasedStorageManager("JsDependencies") - val dfsHandler: MetadataDFSHandler = DependencyMetadataLoader(lookupTracker, metadataVersion, languageSettings, storageManager) - val sortedDeps = DFS.dfs(dependencies, CompiledModule::dependencies, dfsHandler) - val builtInModule = sortedDeps.firstOrNull()?.moduleDescriptor // null in case compiling builtInModule itself + immediateDependencies: List, + allDependencies: List, + outputKlibPath: String +): TranslationResult { + + val deserializedModuleParts: Map = + allDependencies.associateWith { loadKlibMetadataParts(it) } + + fun findModuleByName(name: String): KlibModuleRef = + allDependencies.find { it.moduleName == name } ?: error("Module is not found: $name") + + val moduleDependencies: Map> = + deserializedModuleParts.mapValues { (_, parts) -> + parts.importedModules.map(::findModuleByName) + } + + val sortedImmediateDependencies: List = + DFS.topologicalOrder(immediateDependencies) { moduleDependencies.getValue(it) } + .reversed() + + val depsDescriptors = ModulesStructure( + LookupTracker.DO_NOTHING, + configuration.metadataVersion, + configuration.languageVersionSettings, + moduleDependencies + ) + + val builtInsDep = sortedImmediateDependencies.firstOrNull() + val builtInModuleDescriptor = + if (builtInsDep != null) + depsDescriptors.getModuleDescriptor(builtInsDep) + else + null // null in case compiling builtInModule itself val analysisResult = TopDownAnalyzerFacadeForJS.analyzeFiles( files, project, configuration, - sortedDeps.mapNotNull { it.moduleDescriptor }, - emptyList(), - thisIsBuiltInsModule = builtInModule == null, - customBuiltInsModule = builtInModule + sortedImmediateDependencies.map { depsDescriptors.getModuleDescriptor(it) }, + friendModuleDescriptors = emptyList(), + thisIsBuiltInsModule = builtInModuleDescriptor == null, + customBuiltInsModule = builtInModuleDescriptor ) ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() - TopDownAnalyzerFacadeForJS.checkForErrors(files, analysisResult.bindingContext) val moduleDescriptor = analysisResult.moduleDescriptor as ModuleDescriptorImpl val symbolTable = SymbolTable() - val psi2IrTranslator = Psi2IrTranslator(languageSettings) + val psi2IrTranslator = Psi2IrTranslator(configuration.languageVersionSettings) val psi2IrContext = psi2IrTranslator.createGeneratorContext(moduleDescriptor, analysisResult.bindingContext, symbolTable) val irBuiltIns = psi2IrContext.irBuiltIns var deserializer = JsIrLinker(moduleDescriptor, logggg, irBuiltIns, symbolTable) - val deserializedModuleFragments = sortedDeps.map { + val deserializedModuleFragments = sortedImmediateDependencies.map { val moduleFile = File(it.klibPath, moduleHeaderFileName) - deserializer.deserializeIrModuleHeader(it.moduleDescriptor!!, moduleFile.readBytes(), File(it.klibPath), DeserializationStrategy.ONLY_REFERENCED) + deserializer.deserializeIrModuleHeader(depsDescriptors.getModuleDescriptor(it), moduleFile.readBytes(), File(it.klibPath), DeserializationStrategy.ONLY_REFERENCED) } - var moduleFragment = psi2IrTranslator.generateModuleFragment(psi2IrContext, files, deserializer) - val moduleName = configuration.get(CommonConfigurationKeys.MODULE_NAME) as String + val moduleFragment = psi2IrTranslator.generateModuleFragment(psi2IrContext, files, deserializer) - val context = if (compileMode.generateKlib) { + if (compileMode == CompilationMode.KLIB) { deserializedModuleFragments.forEach { ExternalDependenciesGenerator(it.descriptor, symbolTable, irBuiltIns).generateUnboundSymbolsAsDependencies() } deserializedModuleFragments.forEach { it.patchDeclarationParents() } + val moduleName = configuration.get(CommonConfigurationKeys.MODULE_NAME) as String serializeModuleIntoKlib( moduleName, - metadataVersion, - languageSettings, - symbolTable, + configuration.metadataVersion, + configuration.languageVersionSettings, + psi2IrContext.symbolTable, psi2IrContext.bindingContext, - klibPath, - dependencies, + outputKlibPath, + immediateDependencies, moduleFragment ) - if (compileMode.generateJS) { - deserializeModuleFromKlib( - klibPath, - moduleName, - lookupTracker, - LockBasedStorageManager("JsDeserialized"), - metadataVersion, - languageSettings, - sortedDeps, - builtInModule - ).let { - deserializer = it.deserializer - moduleFragment = it.moduleIr + return TranslationResult.CompiledKlib + } - JsIrBackendContext(it.moduleDescriptor, it.irBuiltIns, it.symbolTable, it.moduleIr, configuration, compileMode).also { - moduleFragment.replaceUnboundSymbols(it) - } - } - } else { - return CompiledModule(moduleName, null, null, klibPath, dependencies, builtInModule == null) - } - } else JsIrBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, configuration, compileMode) - - val jsProgram = if (compileMode.generateJS) { - deserializedModuleFragments.forEach { - ExternalDependenciesGenerator( - it.descriptor, - context.symbolTable, - context.irBuiltIns, - deserializer = deserializer - ).generateUnboundSymbolsAsDependencies() - } - - // TODO: check the order - val irFiles = deserializedModuleFragments.flatMap { it.files } + moduleFragment.files - - moduleFragment.files.clear() - moduleFragment.files += irFiles + val context = JsIrBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, configuration, compileMode) + deserializedModuleFragments.forEach { ExternalDependenciesGenerator( - moduleDescriptor = context.module, - symbolTable = context.symbolTable, - irBuiltIns = context.irBuiltIns + it.descriptor, + symbolTable, + irBuiltIns, + deserializer = deserializer ).generateUnboundSymbolsAsDependencies() - moduleFragment.patchDeclarationParents() + } - jsPhases.invokeToplevel(context.phaseConfig, context, moduleFragment) + // TODO: check the order + val irFiles = deserializedModuleFragments.flatMap { it.files } + moduleFragment.files - moduleFragment.accept(IrModuleToJsTransformer(context), null) - } else null + moduleFragment.files.clear() + moduleFragment.files += irFiles - return CompiledModule(moduleName, jsProgram?.toString(), null, klibPath, dependencies, builtInModule == null) + ExternalDependenciesGenerator( + moduleDescriptor = context.module, + symbolTable = context.symbolTable, + irBuiltIns = context.irBuiltIns + ).generateUnboundSymbolsAsDependencies() + moduleFragment.patchDeclarationParents() + jsPhases.invokeToplevel(context.phaseConfig, context, moduleFragment) + + val jsProgram = moduleFragment.accept(IrModuleToJsTransformer(context), null) + return TranslationResult.CompiledJsCode(jsProgram.toString()) +} + +private fun loadKlibMetadataParts( + moduleId: KlibModuleRef +): JsKlibMetadataParts { + val metadataFile = File(moduleId.klibPath, metadataFileName(moduleId.moduleName)) + val serializer = JsKlibMetadataSerializationUtil + return serializer.readModuleAsProto(metadataFile.readBytes()) } private fun loadKlibMetadata( - moduleName: String, - klibPath: String, + parts: JsKlibMetadataParts, + moduleId: KlibModuleRef, isBuiltIn: Boolean, lookupTracker: LookupTracker, storageManager: LockBasedStorageManager, metadataVersion: JsKlibMetadataVersion, languageVersionSettings: LanguageVersionSettings, builtinsModule: ModuleDescriptorImpl?, - dependencies: List + dependencies: List ): ModuleDescriptorImpl { assert(isBuiltIn == (builtinsModule === null)) - - val metadataFile = File(klibPath, metadataFileName(moduleName)) - - val serializer = JsKlibMetadataSerializationUtil - val parts = serializer.readModuleAsProto(metadataFile.readBytes()) val builtIns = builtinsModule?.builtIns ?: object : KotlinBuiltIns(storageManager) {} - val md = ModuleDescriptorImpl(Name.special("<$moduleName>"), storageManager, builtIns) + val md = ModuleDescriptorImpl(Name.special("<${moduleId.moduleName}>"), storageManager, builtIns) if (isBuiltIn) builtIns.builtInsModule = md val currentModuleFragmentProvider = createJsKlibMetadataPackageFragmentProvider( storageManager, md, parts.header, parts.body, metadataVersion, @@ -280,42 +234,40 @@ private fun loadKlibMetadata( } else currentModuleFragmentProvider md.initialize(packageFragmentProvider) - md.setDependencies(listOf(md) + dependencies.mapNotNull { it.moduleDescriptor }) + md.setDependencies(listOf(md) + dependencies) return md } -typealias MetadataDFSHandler = DFS.NodeHandler> -private class DependencyMetadataLoader( +class ModulesStructure( private val lookupTracker: LookupTracker, private val metadataVersion: JsKlibMetadataVersion, private val languageVersionSettings: LanguageVersionSettings, - private val storageManager: LockBasedStorageManager -) : MetadataDFSHandler { - private val sortedDependencies = mutableListOf() - + private val moduleDependencies: Map> +) { + private val storageManager: LockBasedStorageManager = LockBasedStorageManager("ModulesStructure") private var runtimeModule: ModuleDescriptorImpl? = null - override fun beforeChildren(current: CompiledModule) = true + private val descriptors = mutableMapOf() - override fun afterChildren(current: CompiledModule) { - val md = current.moduleDescriptor ?: loadKlibMetadata( - current.moduleName, - current.klibPath, - current.isBuiltIn, + fun getModuleDescriptor(current: KlibModuleRef): ModuleDescriptorImpl = descriptors.getOrPut(current) { + val parts = loadKlibMetadataParts(current) + val isBuiltIns = parts.importedModules.isEmpty() + loadKlibMetadata( + parts, + current, + isBuiltIns, lookupTracker, storageManager, metadataVersion, languageVersionSettings, runtimeModule, - current.dependencies - ).also { current.moduleDescriptor = it } - sortedDependencies += current - if (current.isBuiltIn) runtimeModule = md + moduleDependencies.getValue(current).map { getModuleDescriptor(it) } + ).also { + if (isBuiltIns) runtimeModule = it + } } - - override fun result() = sortedDependencies } fun serializeModuleIntoKlib( @@ -325,7 +277,7 @@ fun serializeModuleIntoKlib( symbolTable: SymbolTable, bindingContext: BindingContext, klibPath: String, - dependencies: List, + dependencies: List, moduleFragment: IrModuleFragment ) { val declarationTable = JsDeclarationTable(moduleFragment.irBuiltins, DescriptorTable()) @@ -360,4 +312,4 @@ fun serializeModuleIntoKlib( File(klibDir, "${moduleDescription.name}.${JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION}").also { it.writeBytes(serializedData.asByteArray()) } -} \ No newline at end of file +} diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java index 2f51c0335de..98adbca579f 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java @@ -25,6 +25,9 @@ import java.io.File; import java.util.List; public class JSConfigurationKeys { + public static final CompilerConfigurationKey> TRANSITIVE_LIBRARIES = + CompilerConfigurationKey.create("library files for transitive dependencies"); + public static final CompilerConfigurationKey> LIBRARIES = CompilerConfigurationKey.create("library file paths"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateIrRuntime.kt b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateIrRuntime.kt index 44d04a61407..34db2f86c26 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateIrRuntime.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateIrRuntime.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.test.JsIrTestRuntime import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.serialization.js.ModuleKind -import java.io.File fun buildConfiguration(environment: KotlinCoreEnvironment): CompilerConfiguration { val runtimeConfiguration = environment.configuration.copy() @@ -58,17 +57,15 @@ fun main() { fun buildKlib(sources: List, outputPath: String) { - val result = compile( - environment.project, - sources.map(::createPsiFile), - buildConfiguration(environment), - emptyList(), - CompilationMode.KLIB_WITH_JS, - emptyList(), - outputPath + compile( + project = environment.project, + files = sources.map(::createPsiFile), + configuration = buildConfiguration(environment), + compileMode = CompilationMode.KLIB, + immediateDependencies = emptyList(), + allDependencies = emptyList(), + outputKlibPath = outputPath ) - - result.generatedCode?.let { File(outputPath, "result.js").writeText(it) } } buildKlib(JsIrTestRuntime.FULL.sources, fullRuntimeKlibPath) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index 7ae66e9d3ea..381176351b3 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -121,6 +121,10 @@ abstract class BasicBoxTest( .map { it.module }.distinct() .map { it.name to it }.toMap() + fun TestModule.allTransitiveDependencies(): Set { + return dependencies.toSet() + dependencies.flatMap { modules[it]!!.allTransitiveDependencies() } + } + val orderedModules = DFS.topologicalOrder(modules.values) { module -> module.dependencies.mapNotNull { modules[it] } } val testPackage = testFactory.testPackage @@ -131,12 +135,13 @@ abstract class BasicBoxTest( val generatedJsFiles = orderedModules.asReversed().mapNotNull { module -> val dependencies = module.dependencies.map { modules[it]?.outputFileName(outputDir) + ".meta.js" } + val allDependencies = module.allTransitiveDependencies().map { modules[it]?.outputFileName(outputDir) + ".meta.js" } val friends = module.friends.map { modules[it]?.outputFileName(outputDir) + ".meta.js" } val outputFileName = module.outputFileName(outputDir) + ".js" val isMainModule = mainModuleName == module.name generateJavaScriptFile( - file.parent, module, outputFileName, dependencies, friends, modules.size > 1, + file.parent, module, outputFileName, dependencies, allDependencies, friends, modules.size > 1, !SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(), outputPrefixFile, outputPostfixFile, actualMainCallParameters, testPackage, testFunction, runtimeType, isMainModule @@ -314,6 +319,7 @@ abstract class BasicBoxTest( module: TestModule, outputFileName: String, dependencies: List, + allDependencies: List, friends: List, multiModule: Boolean, remap: Boolean, @@ -339,7 +345,7 @@ abstract class BasicBoxTest( val psiFiles = createPsiFiles(allSourceFiles.sortedBy { it.canonicalPath }.map { it.canonicalPath }) val sourceDirs = (testFiles + additionalFiles).map { File(it).parent }.distinct() - val config = createConfig(sourceDirs, module, dependencies, friends, multiModule, incrementalData = null) + val config = createConfig(sourceDirs, module, dependencies, allDependencies, friends, multiModule, incrementalData = null) val outputFile = File(outputFileName) val incrementalData = IncrementalData() @@ -350,7 +356,7 @@ abstract class BasicBoxTest( if (incrementalCompilationChecksEnabled && module.hasFilesToRecompile) { checkIncrementalCompilation( - sourceDirs, module, kotlinFiles, dependencies, friends, multiModule, remap, + sourceDirs, module, kotlinFiles, dependencies, allDependencies, friends, multiModule, remap, outputFile, outputPrefixFile, outputPostfixFile, mainCallParameters, incrementalData, testPackage, testFunction, runtime ) } @@ -361,6 +367,7 @@ abstract class BasicBoxTest( module: TestModule, kotlinFiles: List, dependencies: List, + allDependencies: List, friends: List, multiModule: Boolean, remap: Boolean, @@ -388,7 +395,7 @@ abstract class BasicBoxTest( .sortedBy { it.canonicalPath } .map { sourceToTranslationUnit[it]!! } - val recompiledConfig = createConfig(sourceDirs, module, dependencies, friends, multiModule, incrementalData) + val recompiledConfig = createConfig(sourceDirs, module, dependencies, allDependencies, friends, multiModule, incrementalData) val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js") translateFiles( @@ -593,7 +600,7 @@ abstract class BasicBoxTest( private fun createPsiFiles(fileNames: List): List = fileNames.map(this::createPsiFile) private fun createConfig( - sourceDirs: List, module: TestModule, dependencies: List, friends: List, + sourceDirs: List, module: TestModule, dependencies: List, allDependencies: List, friends: List, multiModule: Boolean, incrementalData: IncrementalData? ): JsConfig { val configuration = environment.configuration.copy() @@ -603,7 +610,14 @@ abstract class BasicBoxTest( configuration.languageVersionSettings = languageVersionSettings } - configuration.put(JSConfigurationKeys.LIBRARIES, JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST + dependencies) + val libraries = when (targetBackend) { + TargetBackend.JS_IR -> dependencies + TargetBackend.JS -> JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST + dependencies + else -> error("Unsupported target backend: $targetBackend") + } + + configuration.put(JSConfigurationKeys.LIBRARIES, libraries) + configuration.put(JSConfigurationKeys.TRANSITIVE_LIBRARIES, allDependencies) configuration.put(JSConfigurationKeys.FRIEND_PATHS, friends) configuration.put(CommonConfigurationKeys.MODULE_NAME, module.name.removeSuffix(OLD_MODULE_SUFFIX)) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index e1fce96e09a..f8b9694ee08 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -5,14 +5,15 @@ package org.jetbrains.kotlin.js.test +import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.ir.backend.js.CompilationMode -import org.jetbrains.kotlin.ir.backend.js.CompiledModule +import org.jetbrains.kotlin.ir.backend.js.KlibModuleRef +import org.jetbrains.kotlin.ir.backend.js.TranslationResult import org.jetbrains.kotlin.ir.backend.js.compile import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.facade.MainCallParameters import org.jetbrains.kotlin.js.facade.TranslationUnit -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.test.TargetBackend import java.io.File @@ -21,8 +22,8 @@ private val defaultRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtim private val JS_IR_RUNTIME_MODULE_NAME = "JS_IR_RUNTIME" -private val fullRuntimeKlib = CompiledModule(JS_IR_RUNTIME_MODULE_NAME, null, null, fullRuntimeKlibPath, emptyList(), true) -private val defaultRuntimeKlib = CompiledModule(JS_IR_RUNTIME_MODULE_NAME, null, null, defaultRuntimeKlibPath, emptyList(), true) +private val fullRuntimeKlib = KlibModuleRef(JS_IR_RUNTIME_MODULE_NAME, fullRuntimeKlibPath) +private val defaultRuntimeKlib = KlibModuleRef(JS_IR_RUNTIME_MODULE_NAME, defaultRuntimeKlibPath) abstract class BasicIrBoxTest( pathToTestDir: String, @@ -45,7 +46,7 @@ abstract class BasicIrBoxTest( // TODO Design incremental compilation for IR and add test support override val incrementalCompilationChecksEnabled = false - private val compilationCache = mutableMapOf() + private val compilationCache = mutableMapOf() override fun doTest(filePath: String, expectedResult: String, mainCallParameters: MainCallParameters, coroutinesPackage: String) { compilationCache.clear() @@ -84,11 +85,18 @@ abstract class BasicIrBoxTest( // ) // ) - val runtimeKlib = runtimes[runtime] + val runtimeKlib = runtimes[runtime]!! - val dependencyNames = config.configuration[JSConfigurationKeys.LIBRARIES]!!.map { File(it).name } - val dependencies = listOfNotNull(runtimeKlib) + dependencyNames.mapNotNull { - compilationCache[it] + val libraries = config.configuration[JSConfigurationKeys.LIBRARIES]!!.map { File(it).name } + val transitiveLibraries = config.configuration[JSConfigurationKeys.TRANSITIVE_LIBRARIES]!!.map { File(it).name } + + // TODO: Add proper depencencies + val dependencies = listOf(runtimeKlib) + libraries.map { + compilationCache[it] ?: error("Can't find compiled module for dependency $it") + } + + val allDependencies = listOf(runtimeKlib) + transitiveLibraries.map { + compilationCache[it] ?: error("Can't find compiled module for dependency $it") } // config.configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE, setOf("UnitMaterializationLowering")) @@ -100,21 +108,23 @@ abstract class BasicIrBoxTest( if (!isMainModule) it.replace("_v5.js", "/") else it } - val result = compile( - config.project, - filesToCompile, - config.configuration, - listOf(FqName((testPackage?.let { "$it." } ?: "") + testFunction)), - if (isMainModule) CompilationMode.JS_AGAINST_KLIB else CompilationMode.KLIB, - dependencies, - actualOutputFile + val result: TranslationResult = compile( + project = config.project, + files = filesToCompile, + configuration = config.configuration, + compileMode = if (isMainModule) CompilationMode.JS else CompilationMode.KLIB, + immediateDependencies = dependencies, + allDependencies = allDependencies, + outputKlibPath = actualOutputFile ) - compilationCache[outputFile.name.replace(".js", ".meta.js")] = result + val moduleName = config.configuration.get(CommonConfigurationKeys.MODULE_NAME) as String + val module = KlibModuleRef(moduleName, actualOutputFile) - val generatedCode = result.generatedCode - if (generatedCode != null) { - val wrappedCode = wrapWithModuleEmulationMarkers(generatedCode, moduleId = config.moduleId, moduleKind = config.moduleKind) + compilationCache[outputFile.name.replace(".js", ".meta.js")] = module + + if (result is TranslationResult.CompiledJsCode) { + val wrappedCode = wrapWithModuleEmulationMarkers(result.jsCode, moduleId = config.moduleId, moduleKind = config.moduleKind) outputFile.write(wrappedCode) } }