[Commonizer] Fast-pass for library fragments absent for some targets

^KT-39897
This commit is contained in:
Dmitriy Dolovov
2020-06-25 13:45:38 +07:00
parent ee22488ab2
commit b2022144e6
15 changed files with 271 additions and 132 deletions
@@ -6,19 +6,20 @@
package org.jetbrains.kotlin.descriptors.commonizer package org.jetbrains.kotlin.descriptors.commonizer
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import java.io.File
sealed class Result sealed class Result {
object NothingToCommonize : Result()
object NothingToCommonize : Result() class Commonized(
val modulesByTargets: Map<Target, Collection<ModuleResult>>
class CommonizationPerformed( ) : Result() {
val modulesByTargets: Map<Target, Collection<ModuleDescriptor>> val sharedTarget: OutputTarget by lazy { modulesByTargets.keys.filterIsInstance<OutputTarget>().single() }
) : Result() { val leafTargets: Set<InputTarget> by lazy { modulesByTargets.keys.filterIsInstance<InputTarget>().toSet() }
val commonTarget: OutputTarget by lazy {
modulesByTargets.keys.filterIsInstance<OutputTarget>().single()
}
val concreteTargets: Set<InputTarget> by lazy {
modulesByTargets.keys.filterIsInstance<InputTarget>().toSet()
} }
} }
sealed class ModuleResult {
class Absent(val originalLocation: File) : ModuleResult()
class Commonized(val module: ModuleDescriptor) : ModuleResult()
}
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.descriptors.commonizer package org.jetbrains.kotlin.descriptors.commonizer
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import java.io.File
class TargetProvider( class TargetProvider(
val target: InputTarget, val target: InputTarget,
@@ -19,20 +21,15 @@ interface BuiltInsProvider {
fun loadBuiltIns(): KotlinBuiltIns fun loadBuiltIns(): KotlinBuiltIns
companion object { companion object {
fun wrap(builtIns: KotlinBuiltIns) = object : BuiltInsProvider { val defaultBuiltInsProvider = object : BuiltInsProvider {
override fun loadBuiltIns() = builtIns override fun loadBuiltIns() = DefaultBuiltIns.Instance
} }
} }
} }
interface ModulesProvider { interface ModulesProvider {
class ModuleInfo(val name: String, val originalLocation: File)
fun loadModuleInfos(): Map<String, ModuleInfo>
fun loadModules(): Collection<ModuleDescriptor> fun loadModules(): Collection<ModuleDescriptor>
companion object {
fun wrap(modules: Collection<ModuleDescriptor>) = object : ModulesProvider {
override fun loadModules() = modules
}
fun wrap(vararg modules: ModuleDescriptor) = wrap(modules.toList())
}
} }
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirNode.Companion.
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirNode.Companion.indexOfCommon import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirNode.Companion.indexOfCommon
import org.jetbrains.kotlin.descriptors.commonizer.utils.CommonizedGroup import org.jetbrains.kotlin.descriptors.commonizer.utils.CommonizedGroup
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.utils.addIfNotNull
/** /**
* Serves two goals: * Serves two goals:
@@ -28,16 +27,17 @@ internal class DeclarationsBuilderVisitor1(
check(data.isEmpty()) // root node may not have containing declarations check(data.isEmpty()) // root node may not have containing declarations
check(components.targetComponents.size == node.dimension) check(components.targetComponents.size == node.dimension)
val allTargets = (node.targetDeclarations + node.commonDeclaration()).map { it!!.target } val allTargets: List<Target> = (node.targetDeclarations + node.commonDeclaration()).map { it!!.target }
val modulesByTargets: Map<Target, MutableList<ModuleDescriptorImpl>> = allTargets.associateWithTo(HashMap()) { mutableListOf() }
val modulesByTargets = HashMap<Target, MutableList<ModuleDescriptorImpl>>()
// collect module descriptors: // collect module descriptors:
for (moduleNode in node.modules.values) { for (moduleNode in node.modules.values) {
val modules = moduleNode.accept(this, noContainingDeclarations()).asListContaining<ModuleDescriptorImpl>() val modules = moduleNode.accept(this, noContainingDeclarations()).asListContaining<ModuleDescriptorImpl>()
modules.forEachIndexed { index, module -> modules.forEachIndexed { index, module ->
val target = allTargets[index] if (module != null) {
modulesByTargets.getOrPut(target) { mutableListOf() }.addIfNotNull(module) val target = allTargets[index]
modulesByTargets.getValue(target) += module
}
} }
} }
@@ -50,9 +50,9 @@ private fun parseTask(
fun buildOngoingTask() { fun buildOngoingTask() {
// check options completeness // check options completeness
val missedMandatoryOptions = optionTypes.filterKeys { it !in options }.filterValues { it.mandatory }.keys val missingMandatoryOptions = optionTypes.filterKeys { it !in options }.filterValues { it.mandatory }.keys
if (missedMandatoryOptions.isNotEmpty()) if (missingMandatoryOptions.isNotEmpty())
printUsageAndExit("Mandatory options not specified in task $taskAlias: " + missedMandatoryOptions.joinToString { "-$it" }) printUsageAndExit("Mandatory options not specified in task $taskAlias: " + missingMandatoryOptions.joinToString { "-$it" })
tasks += taskType.taskConstructor(options.values) tasks += taskType.taskConstructor(options.values)
} }
@@ -20,7 +20,7 @@ class RootCommonizer : AbstractStandardCommonizer<CirRoot, CirRoot>() {
override fun commonizationResult() = CirRootFactory.create( override fun commonizationResult() = CirRootFactory.create(
target = OutputTarget(inputTargets), target = OutputTarget(inputTargets),
builtInsClass = if (konanBuiltInsProvider != null) KonanBuiltIns::class.java.name else DefaultBuiltIns::class.java.name, builtInsClass = if (konanBuiltInsProvider != null) KonanBuiltIns::class.java.name else DefaultBuiltIns::class.java.name,
builtInsProvider = konanBuiltInsProvider ?: BuiltInsProvider.wrap(DefaultBuiltIns.Instance) builtInsProvider = konanBuiltInsProvider ?: BuiltInsProvider.defaultBuiltInsProvider
) )
override fun initialize(first: CirRoot) { override fun initialize(first: CirRoot) {
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.descriptors.commonizer package org.jetbrains.kotlin.descriptors.commonizer
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.commonizer.builder.DeclarationsBuilderVisitor1 import org.jetbrains.kotlin.descriptors.commonizer.builder.DeclarationsBuilderVisitor1
import org.jetbrains.kotlin.descriptors.commonizer.builder.DeclarationsBuilderVisitor2 import org.jetbrains.kotlin.descriptors.commonizer.builder.DeclarationsBuilderVisitor2
import org.jetbrains.kotlin.descriptors.commonizer.builder.createGlobalBuilderComponents import org.jetbrains.kotlin.descriptors.commonizer.builder.createGlobalBuilderComponents
@@ -15,14 +14,15 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager
fun runCommonization(parameters: Parameters): Result { fun runCommonization(parameters: Parameters): Result {
if (!parameters.hasAnythingToCommonize()) if (!parameters.hasAnythingToCommonize())
return NothingToCommonize return Result.NothingToCommonize
val storageManager = LockBasedStorageManager("Declaration descriptors commonization") val storageManager = LockBasedStorageManager("Declaration descriptors commonization")
// build merged tree: // build merged tree:
val mergedTree = CirTreeMerger(storageManager, parameters).merge() val mergeResult = CirTreeMerger(storageManager, parameters).merge()
// commonize: // commonize:
val mergedTree = mergeResult.root
mergedTree.accept(CommonizationVisitor(mergedTree), Unit) mergedTree.accept(CommonizationVisitor(mergedTree), Unit)
parameters.progressLogger?.invoke("Commonized declarations") parameters.progressLogger?.invoke("Commonized declarations")
@@ -31,15 +31,21 @@ fun runCommonization(parameters: Parameters): Result {
mergedTree.accept(DeclarationsBuilderVisitor1(components), emptyList()) mergedTree.accept(DeclarationsBuilderVisitor1(components), emptyList())
mergedTree.accept(DeclarationsBuilderVisitor2(components), emptyList()) mergedTree.accept(DeclarationsBuilderVisitor2(components), emptyList())
val modulesByTargets = LinkedHashMap<Target, Collection<ModuleDescriptor>>() // use linked hash map to preserve order val modulesByTargets = LinkedHashMap<Target, Collection<ModuleResult>>() // use linked hash map to preserve order
components.targetComponents.forEach { components.targetComponents.forEach { component ->
val target = it.target val target = component.target
check(target !in modulesByTargets) check(target !in modulesByTargets)
modulesByTargets[target] = components.cache.getAllModules(it.index) val commonizedModules: List<ModuleResult.Commonized> = components.cache.getAllModules(component.index).map(ModuleResult::Commonized)
val absentModules: List<ModuleResult.Absent> = if (target is InputTarget)
mergeResult.absentModuleInfos.getValue(target).map { ModuleResult.Absent(it.originalLocation) }
else emptyList()
modulesByTargets[target] = commonizedModules + absentModules
} }
parameters.progressLogger?.invoke("Prepared new descriptors") parameters.progressLogger?.invoke("Prepared new descriptors")
return CommonizationPerformed(modulesByTargets) return Result.Commonized(modulesByTargets)
} }
@@ -91,9 +91,9 @@ class NativeDistributionCommonizer(
val stdlib = loadLibrary(stdlibPath) val stdlib = loadLibrary(stdlibPath)
val result = targets.associate { target -> val result = targets.associate { target ->
val inputTarget = InputTarget(target.name, target) val leafTarget = InputTarget(target.name, target)
val platformLibs = inputTarget.platformLibrariesSource val platformLibs = leafTarget.platformLibrariesSource
.takeIf { it.isDirectory } .takeIf { it.isDirectory }
?.listFiles() ?.listFiles()
?.takeIf { it.isNotEmpty() } ?.takeIf { it.isNotEmpty() }
@@ -103,7 +103,7 @@ class NativeDistributionCommonizer(
if (platformLibs.isEmpty()) if (platformLibs.isEmpty())
logger.warning("No platform libraries found for target $target. This target will be excluded from commonization.") logger.warning("No platform libraries found for target $target. This target will be excluded from commonization.")
inputTarget to NativeDistributionLibraries(stdlib, platformLibs) leafTarget to NativeDistributionLibraries(stdlib, platformLibs)
} }
logProgress("Read lazy (uninitialized) libraries") logProgress("Read lazy (uninitialized) libraries")
@@ -176,16 +176,16 @@ class NativeDistributionCommonizer(
copyCommonStandardLibraries() copyCommonStandardLibraries()
when (result) { when (result) {
is NothingToCommonize -> { is Result.NothingToCommonize -> {
// It may happen that all targets to be commonized (or at least all but one target) miss platform libraries. // It may happen that all targets to be commonized (or at least all but one target) miss platform libraries.
// In such case commonizer will do nothing and return a special result value 'NothingToCommonize'. // In such case commonizer will do nothing and return a special result value 'NothingToCommonize'.
// So, let's just copy platform libraries from those target where they are to the new destination. // So, let's just copy platform libraries from the target where they are to the new destination.
originalLibrariesByTargets.forEach { (target, libraries) -> originalLibrariesByTargets.forEach { (target, libraries) ->
copyTargetAsIs(target, libraries.platformLibs.size) copyTargetAsIs(target, libraries.platformLibs.size)
} }
} }
is CommonizationPerformed -> { is Result.Commonized -> {
val serializer = KlibMetadataMonolithicSerializer( val serializer = KlibMetadataMonolithicSerializer(
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT, languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
metadataVersion = KlibMetadataVersion.INSTANCE, metadataVersion = KlibMetadataVersion.INSTANCE,
@@ -193,7 +193,7 @@ class NativeDistributionCommonizer(
) )
// 'targetsToCopy' are some targets with empty set of platform libraries // 'targetsToCopy' are some targets with empty set of platform libraries
val targetsToCopy = originalLibrariesByTargets.keys - result.concreteTargets val targetsToCopy = originalLibrariesByTargets.keys - result.leafTargets
if (targetsToCopy.isNotEmpty()) { if (targetsToCopy.isNotEmpty()) {
targetsToCopy.forEach { target -> targetsToCopy.forEach { target ->
val libraries = originalLibrariesByTargets.getValue(target) val libraries = originalLibrariesByTargets.getValue(target)
@@ -201,11 +201,13 @@ class NativeDistributionCommonizer(
} }
} }
val leafTargetNames = result.leafTargets.map { it.name }
val concreteTargetNames = result.concreteTargets.map { it.name } val targetsToSerialize = result.leafTargets + result.sharedTarget
val targetsToSerialize = result.concreteTargets + result.commonTarget
targetsToSerialize.forEach { target -> targetsToSerialize.forEach { target ->
val newModules = result.modulesByTargets.getValue(target) val moduleResults: Collection<ModuleResult> = result.modulesByTargets.getValue(target)
val newModules: Collection<ModuleDescriptor> = moduleResults.mapNotNull { (it as? ModuleResult.Commonized)?.module }
val absentModuleLocations: List<File> =
moduleResults.mapNotNull { (it as? ModuleResult.Absent)?.originalLocation }
val manifestProvider: NativeManifestDataProvider val manifestProvider: NativeManifestDataProvider
val starredTarget: String? val starredTarget: String?
@@ -220,8 +222,8 @@ class NativeDistributionCommonizer(
} }
} }
val targetName = concreteTargetNames.joinToString { if (it == starredTarget) "$it(*)" else it } val targetName = leafTargetNames.joinToString { if (it == starredTarget) "$it(*)" else it }
serializeTarget(target, targetName, newModules, manifestProvider, serializer) serializeTarget(target, targetName, newModules, absentModuleLocations, manifestProvider, serializer)
} }
} }
} }
@@ -252,24 +254,26 @@ class NativeDistributionCommonizer(
} }
} }
private fun copyTargetAsIs(target: InputTarget, platformLibrariesCount: Int) { private fun copyTargetAsIs(leafTarget: InputTarget, librariesCount: Int) {
val librariesDestination = target.librariesDestination val librariesDestination = leafTarget.librariesDestination
librariesDestination.mkdirs() // always create an empty directory even if there is nothing to copy librariesDestination.mkdirs() // always create an empty directory even if there is nothing to copy
val librariesSource = target.platformLibrariesSource val librariesSource = leafTarget.platformLibrariesSource
if (librariesSource.isDirectory) librariesSource.copyRecursively(librariesDestination) if (librariesSource.isDirectory) librariesSource.copyRecursively(librariesDestination)
logProgress("Copied $platformLibrariesCount libraries for [${target.name}]") logProgress("Copied $librariesCount libraries for [${leafTarget.name}]")
} }
private fun serializeTarget( private fun serializeTarget(
target: Target, target: Target,
targetName: String, targetName: String,
newModules: Collection<ModuleDescriptor>, newModules: Collection<ModuleDescriptor>,
absentModuleLocations: List<File>,
manifestProvider: NativeManifestDataProvider, manifestProvider: NativeManifestDataProvider,
serializer: KlibMetadataMonolithicSerializer serializer: KlibMetadataMonolithicSerializer
) { ) {
val librariesDestination = target.librariesDestination val librariesDestination = target.librariesDestination
librariesDestination.mkdirs() // always create an empty directory even if there is nothing to copy
for (newModule in newModules) { for (newModule in newModules) {
val libraryName = newModule.name val libraryName = newModule.name
@@ -286,6 +290,11 @@ class NativeDistributionCommonizer(
writeLibrary(metadata, manifestData, libraryDestination) writeLibrary(metadata, manifestData, libraryDestination)
} }
for (absentModuleLocation in absentModuleLocations) {
val libraryName = absentModuleLocation.name
absentModuleLocation.copyRecursively(librariesDestination.resolve(libraryName))
}
logProgress("Written libraries for [$targetName]") logProgress("Written libraries for [$targetName]")
} }
@@ -10,11 +10,13 @@ import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.commonizer.BuiltInsProvider import org.jetbrains.kotlin.descriptors.commonizer.BuiltInsProvider
import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider
import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider.ModuleInfo
import org.jetbrains.kotlin.descriptors.commonizer.utils.NativeFactories import org.jetbrains.kotlin.descriptors.commonizer.utils.NativeFactories
import org.jetbrains.kotlin.descriptors.commonizer.utils.createKotlinNativeForwardDeclarationsModule import org.jetbrains.kotlin.descriptors.commonizer.utils.createKotlinNativeForwardDeclarationsModule
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.StorageManager
import java.io.File
internal class NativeDistributionModulesProvider( internal class NativeDistributionModulesProvider(
private val storageManager: StorageManager, private val storageManager: StorageManager,
@@ -32,6 +34,15 @@ internal class NativeDistributionModulesProvider(
return stdlib.builtIns return stdlib.builtIns
} }
override fun loadModuleInfos(): Map<String, ModuleInfo> {
return libraries.platformLibs.associate { library ->
val name = library.manifestData.uniqueName
val location = File(library.library.libraryFile.path)
name to ModuleInfo(name, location)
}
}
override fun loadModules(): Collection<ModuleDescriptor> { override fun loadModules(): Collection<ModuleDescriptor> {
val builtIns = loadBuiltIns() val builtIns = loadBuiltIns()
val stdlib = builtIns.builtInsModule val stdlib = builtIns.builtInsModule
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.descriptors.commonizer.mergedtree package org.jetbrains.kotlin.descriptors.commonizer.mergedtree
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.commonizer.InputTarget
import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider.ModuleInfo
import org.jetbrains.kotlin.descriptors.commonizer.Parameters import org.jetbrains.kotlin.descriptors.commonizer.Parameters
import org.jetbrains.kotlin.descriptors.commonizer.TargetProvider import org.jetbrains.kotlin.descriptors.commonizer.TargetProvider
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirClass import org.jetbrains.kotlin.descriptors.commonizer.cir.CirClass
@@ -23,26 +25,44 @@ class CirTreeMerger(
private val storageManager: StorageManager, private val storageManager: StorageManager,
private val parameters: Parameters private val parameters: Parameters
) { ) {
class CirTreeMergeResult(
val root: CirRootNode,
val absentModuleInfos: Map<InputTarget, Collection<ModuleInfo>>
)
private val size = parameters.targetProviders.size private val size = parameters.targetProviders.size
private lateinit var cacheRW: CirClassifiersCacheImpl private lateinit var cacheRW: CirClassifiersCacheImpl
fun merge(): CirRootNode { fun merge(): CirTreeMergeResult {
val rootNode: CirRootNode = buildRootNode(storageManager, size) val rootNode: CirRootNode = buildRootNode(storageManager, size)
cacheRW = rootNode.cache cacheRW = rootNode.cache
val allModuleInfos: List<Map<String, ModuleInfo>> = parameters.targetProviders.map { it.modulesProvider.loadModuleInfos() }
val commonModuleNames = allModuleInfos.map { it.keys }.reduce { a, b -> a intersect b }
parameters.targetProviders.forEachIndexed { targetIndex, targetProvider -> parameters.targetProviders.forEachIndexed { targetIndex, targetProvider ->
processTarget(rootNode, targetIndex, targetProvider) processTarget(rootNode, targetIndex, targetProvider, commonModuleNames)
parameters.progressLogger?.invoke("Loaded declarations for [${targetProvider.target.name}]") parameters.progressLogger?.invoke("Loaded declarations for [${targetProvider.target.name}]")
System.gc() System.gc()
} }
return rootNode val absentModuleInfos = allModuleInfos.mapIndexed { index, moduleInfos ->
val target = parameters.targetProviders[index].target
val absentInfos = moduleInfos.filterKeys { name -> name !in commonModuleNames }.values
target to absentInfos
}.toMap()
return CirTreeMergeResult(
root = rootNode,
absentModuleInfos = absentModuleInfos
)
} }
private fun processTarget( private fun processTarget(
rootNode: CirRootNode, rootNode: CirRootNode,
targetIndex: Int, targetIndex: Int,
targetProvider: TargetProvider targetProvider: TargetProvider,
commonModuleNames: Set<String>
) { ) {
rootNode.targetDeclarations[targetIndex] = CirRootFactory.create( rootNode.targetDeclarations[targetIndex] = CirRootFactory.create(
targetProvider.target, targetProvider.target,
@@ -54,7 +74,8 @@ class CirTreeMerger(
val modules: MutableMap<Name, CirModuleNode> = rootNode.modules val modules: MutableMap<Name, CirModuleNode> = rootNode.modules
moduleDescriptors.forEach { moduleDescriptor -> moduleDescriptors.forEach { moduleDescriptor ->
processModule(modules, targetIndex, moduleDescriptor) if (moduleDescriptor.name.asString().removeSurrounding("<", ">") in commonModuleNames)
processModule(modules, targetIndex, moduleDescriptor)
} }
} }
@@ -40,8 +40,8 @@ class AggregatedStatsCollector(
"Lifted Up, %%", "Lifted Up, %%",
"Commonized", "Commonized",
"Commonized, %%", "Commonized, %%",
"Missed in s. targets", "Missing in s. targets",
"Missed in s. targets, %%", "Missing in s. targets, %%",
"Failed: Other", "Failed: Other",
"Failed: Other, %%", "Failed: Other, %%",
"Total" "Total"
@@ -19,10 +19,8 @@ import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.commonizer.SourceModuleRoot.Companion.COMMON_TARGET_NAME import org.jetbrains.kotlin.descriptors.commonizer.SourceModuleRoot.Companion.COMMON_TARGET_NAME
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertCommonizationPerformed import org.jetbrains.kotlin.descriptors.commonizer.utils.*
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertIsDirectory import org.jetbrains.kotlin.descriptors.commonizer.utils.MockBuiltInsProvider
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertModulesAreEqual
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertValidModule
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.CommonPlatforms import org.jetbrains.kotlin.platform.CommonPlatforms
@@ -42,10 +40,6 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
init { init {
System.setProperty("java.awt.headless", "true") System.setProperty("java.awt.headless", "true")
} }
fun Collection<ModuleDescriptor>.eachModuleAsTarget() = mapIndexed { index, moduleDescriptor ->
InputTarget("target_$index") to moduleDescriptor
}.toMap().toCommonizationParameters()
} }
private fun getTestDataDir(): File { private fun getTestDataDir(): File {
@@ -69,26 +63,28 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
val result: Result = runCommonization(analyzedModules.toCommonizationParameters()) val result: Result = runCommonization(analyzedModules.toCommonizationParameters())
assertCommonizationPerformed(result) assertCommonizationPerformed(result)
val commonTarget: OutputTarget = analyzedModules.commonizedCommonModule.target val sharedTarget: OutputTarget = analyzedModules.commonizedCommonModule.target
assertEquals(commonTarget, result.commonTarget) assertEquals(sharedTarget, result.sharedTarget)
val commonModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedCommonModule.module val sharedModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedCommonModule.module
val commonModuleByCommonizer: ModuleDescriptor = result.modulesByTargets.getValue(commonTarget).single() val sharedModuleByCommonizer: ModuleDescriptor =
(result.modulesByTargets.getValue(sharedTarget).single() as ModuleResult.Commonized).module
assertValidModule(commonModuleAsExpected) assertValidModule(sharedModuleAsExpected)
assertValidModule(commonModuleByCommonizer) assertValidModule(sharedModuleByCommonizer)
assertModulesAreEqual(commonModuleAsExpected, commonModuleByCommonizer, "\"$commonTarget\" target") assertModulesAreEqual(sharedModuleAsExpected, sharedModuleByCommonizer, "\"$sharedTarget\" target")
val concreteTargets: Set<InputTarget> = analyzedModules.commonizedPlatformModules.keys val leafTargets: Set<InputTarget> = analyzedModules.commonizedPlatformModules.keys
assertEquals(concreteTargets, result.concreteTargets) assertEquals(leafTargets, result.leafTargets)
for (target in concreteTargets) { for (leafTarget in leafTargets) {
val targetModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedPlatformModules.getValue(target).module val leafTargetModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedPlatformModules.getValue(leafTarget).module
val targetModuleByCommonizer: ModuleDescriptor = result.modulesByTargets.getValue(target).single() val leafTargetModuleByCommonizer: ModuleDescriptor =
(result.modulesByTargets.getValue(leafTarget).single() as ModuleResult.Commonized).module
assertValidModule(targetModuleAsExpected) assertValidModule(leafTargetModuleAsExpected)
assertValidModule(targetModuleByCommonizer) assertValidModule(leafTargetModuleByCommonizer)
assertModulesAreEqual(targetModuleAsExpected, targetModuleByCommonizer, "\"$target\" target") assertModulesAreEqual(leafTargetModuleAsExpected, leafTargetModuleByCommonizer, "\"$leafTarget\" target")
} }
} }
} }
@@ -327,8 +323,8 @@ private fun Map<InputTarget, ModuleDescriptor>.toCommonizationParameters(): Para
TargetProvider( TargetProvider(
target = target, target = target,
builtInsClass = moduleDescriptor.builtIns::class.java, builtInsClass = moduleDescriptor.builtIns::class.java,
builtInsProvider = BuiltInsProvider.wrap(moduleDescriptor.builtIns), builtInsProvider = MockBuiltInsProvider(moduleDescriptor.builtIns),
modulesProvider = ModulesProvider.wrap(moduleDescriptor) modulesProvider = MockModulesProvider(moduleDescriptor)
) )
) )
} }
@@ -5,10 +5,9 @@
package org.jetbrains.kotlin.descriptors.commonizer package org.jetbrains.kotlin.descriptors.commonizer
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.descriptors.commonizer.AbstractCommonizationFromSourcesTest.Companion.eachModuleAsTarget import org.jetbrains.kotlin.descriptors.commonizer.utils.MockModulesProvider
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertCommonizationPerformed import org.jetbrains.kotlin.descriptors.commonizer.utils.assertCommonizationPerformed
import org.jetbrains.kotlin.descriptors.commonizer.utils.mockEmptyModule
import org.junit.Test import org.junit.Test
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -17,49 +16,120 @@ import kotlin.test.assertEquals
class CommonizerFacadeTest { class CommonizerFacadeTest {
@Test @Test
fun nothingToCommonize0() { fun nothingToCommonize0() = doTestNothingToCommonize(
val modules = listOf<ModuleDescriptor>() emptyMap()
)
val result = runCommonization(modules.eachModuleAsTarget())
assertEquals(NothingToCommonize, result)
}
@Test @Test
fun nothingToCommonize1() { fun nothingToCommonize1() = doTestNothingToCommonize(
val modules = listOf( mapOf(
mockEmptyModule("<foo>") "target1" to listOf("foo")
) )
)
val result = runCommonization(modules.eachModuleAsTarget())
assertEquals(NothingToCommonize, result)
}
@Test @Test
fun commonized() { fun commonized1() = doTestSuccessfulCommonization(
val modules = listOf( mapOf(
mockEmptyModule("<foo>"), "target1" to listOf("foo"),
mockEmptyModule("<foo>") "target2" to listOf("foo")
) )
)
val result = runCommonization(modules.eachModuleAsTarget()) @Test
fun commonized2() = doTestSuccessfulCommonization(
mapOf(
"target1" to listOf("foo", "bar"),
"target2" to listOf("bar", "foo")
)
)
assertCommonizationPerformed(result) @Test
fun commonizedWithDifferentModules() = doTestSuccessfulCommonization(
mapOf(
"target1" to listOf("foo"),
"target2" to listOf("bar")
)
)
assertSingleModuleForTarget("<foo>", result.modulesByTargets.getValue(result.commonTarget)) @Test
fun commonizedWithAbsentModules() = doTestSuccessfulCommonization(
mapOf(
"target1" to listOf("foo", "bar"),
"target2" to listOf("foo", "qix")
)
)
assertEquals(2, result.concreteTargets.size) companion object {
for (target in result.concreteTargets) { private fun Map<String, List<String>>.toCommonizationParameters() = Parameters().also {
assertSingleModuleForTarget("<foo>", result.modulesByTargets.getValue(target)) forEach { (targetName, moduleNames) ->
it.addTarget(
TargetProvider(
target = InputTarget(targetName),
builtInsClass = DefaultBuiltIns::class.java,
builtInsProvider = BuiltInsProvider.defaultBuiltInsProvider,
modulesProvider = MockModulesProvider(moduleNames)
)
)
}
}
private fun doTestNothingToCommonize(originalModules: Map<String, List<String>>) {
val result = runCommonization(originalModules.toCommonizationParameters())
assertEquals(Result.NothingToCommonize, result)
}
private fun doTestSuccessfulCommonization(originalModules: Map<String, List<String>>) {
val result = runCommonization(originalModules.toCommonizationParameters())
assertCommonizationPerformed(result)
val expectedCommonModuleNames = mutableSetOf<String>()
originalModules.values.forEachIndexed { index, moduleNames ->
if (index == 0)
expectedCommonModuleNames.addAll(moduleNames)
else
expectedCommonModuleNames.retainAll(moduleNames)
}
assertModulesMatch(
expectedCommonizedModuleNames = expectedCommonModuleNames,
expectedAbsentModuleNames = emptySet(),
actualModuleResults = result.modulesByTargets.getValue(result.sharedTarget)
)
result.leafTargets.forEach { target ->
val allModuleNames = originalModules.getValue(target.name).toSet()
val expectedAbsentModuleNames = allModuleNames - expectedCommonModuleNames
assertModulesMatch(
expectedCommonizedModuleNames = expectedCommonModuleNames,
expectedAbsentModuleNames = expectedAbsentModuleNames,
actualModuleResults = result.modulesByTargets.getValue(target)
)
}
}
private fun assertModulesMatch(
expectedCommonizedModuleNames: Set<String>,
expectedAbsentModuleNames: Set<String>,
actualModuleResults: Collection<ModuleResult>
) {
assertEquals(expectedCommonizedModuleNames.size + expectedAbsentModuleNames.size, actualModuleResults.size)
val actualCommonizedModuleNames = mutableSetOf<String>()
val actualAbsentModuleNames = mutableSetOf<String>()
actualModuleResults.forEach { moduleResult ->
when (moduleResult) {
is ModuleResult.Commonized -> {
actualCommonizedModuleNames += moduleResult.module.name.asString().removeSurrounding("<", ">")
}
is ModuleResult.Absent -> {
actualAbsentModuleNames += moduleResult.originalLocation.name
}
}
}
assertEquals(expectedCommonizedModuleNames, actualCommonizedModuleNames)
assertEquals(expectedAbsentModuleNames, actualAbsentModuleNames)
} }
} }
private fun assertSingleModuleForTarget(
@Suppress("SameParameterValue") expectedModuleName: String,
modules: Collection<ModuleDescriptor>
) {
assertEquals(1, modules.size)
assertEquals(expectedModuleName, modules.single().name.asString())
}
} }
@@ -9,12 +9,12 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.descriptors.commonizer.BuiltInsProvider
import org.jetbrains.kotlin.descriptors.commonizer.InputTarget import org.jetbrains.kotlin.descriptors.commonizer.InputTarget
import org.jetbrains.kotlin.descriptors.commonizer.OutputTarget import org.jetbrains.kotlin.descriptors.commonizer.OutputTarget
import org.jetbrains.kotlin.descriptors.commonizer.Target import org.jetbrains.kotlin.descriptors.commonizer.Target
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirRoot import org.jetbrains.kotlin.descriptors.commonizer.cir.CirRoot
import org.jetbrains.kotlin.descriptors.commonizer.cir.factory.CirRootFactory import org.jetbrains.kotlin.descriptors.commonizer.cir.factory.CirRootFactory
import org.jetbrains.kotlin.descriptors.commonizer.utils.MockBuiltInsProvider
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.junit.Test import org.junit.Test
@@ -167,7 +167,7 @@ class RootCommonizerTest : AbstractCommonizerTest<CirRoot, CirRoot>() {
fun KotlinBuiltIns.toMock(target: Target) = CirRootFactory.create( fun KotlinBuiltIns.toMock(target: Target) = CirRootFactory.create(
target = target, target = target,
builtInsClass = this::class.java.name, builtInsClass = this::class.java.name,
builtInsProvider = BuiltInsProvider.wrap(this) builtInsProvider = MockBuiltInsProvider(this)
) )
} }
} }
@@ -6,9 +6,7 @@
package org.jetbrains.kotlin.descriptors.commonizer.utils package org.jetbrains.kotlin.descriptors.commonizer.utils
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.commonizer.CommonizationPerformed
import org.jetbrains.kotlin.descriptors.commonizer.Result import org.jetbrains.kotlin.descriptors.commonizer.Result
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.test.util.DescriptorValidator.* import org.jetbrains.kotlin.test.util.DescriptorValidator.*
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.ErrorUtils
@@ -26,11 +24,11 @@ fun assertIsDirectory(file: File) {
@ExperimentalContracts @ExperimentalContracts
fun assertCommonizationPerformed(result: Result) { fun assertCommonizationPerformed(result: Result) {
contract { contract {
returns() implies (result is CommonizationPerformed) returns() implies (result is Result.Commonized)
} }
if (result !is CommonizationPerformed) if (result !is Result.Commonized)
fail("$result is not instance of ${CommonizationPerformed::class}") fail("$result is not instance of ${Result.Commonized::class}")
} }
@ExperimentalContracts @ExperimentalContracts
@@ -6,9 +6,13 @@
package org.jetbrains.kotlin.descriptors.commonizer.utils package org.jetbrains.kotlin.descriptors.commonizer.utils
import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.commonizer.BuiltInsProvider
import org.jetbrains.kotlin.descriptors.commonizer.InputTarget import org.jetbrains.kotlin.descriptors.commonizer.InputTarget
import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider
import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider.ModuleInfo
import org.jetbrains.kotlin.descriptors.commonizer.builder.* import org.jetbrains.kotlin.descriptors.commonizer.builder.*
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassNode import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassNode
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCache import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassifiersCache
@@ -19,6 +23,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import java.io.File
import kotlin.random.Random import kotlin.random.Random
// expected special name for module // expected special name for module
@@ -118,3 +123,28 @@ internal val EMPTY_CLASSIFIERS_CACHE = object : CirClassifiersCache {
override val classes: Map<FqName, CirClassNode> get() = emptyMap() override val classes: Map<FqName, CirClassNode> get() = emptyMap()
override val typeAliases: Map<FqName, CirTypeAliasNode> get() = emptyMap() override val typeAliases: Map<FqName, CirTypeAliasNode> get() = emptyMap()
} }
internal class MockBuiltInsProvider(private val builtIns: KotlinBuiltIns) : BuiltInsProvider {
override fun loadBuiltIns() = builtIns
}
internal class MockModulesProvider : ModulesProvider {
private val moduleInfos: Map<String, ModuleInfo>
private val modules: Map<String, ModuleDescriptor>
constructor(moduleNames: Collection<String>) {
moduleInfos = moduleNames.associateWith { name -> fakeModuleInfo(name) }
modules = moduleNames.associateWith { name -> mockEmptyModule("<$name>") }
}
constructor(module: ModuleDescriptor) {
val name = module.name.asString().removeSurrounding("<", ">")
moduleInfos = mapOf(name to fakeModuleInfo(name))
modules = mapOf(name to module)
}
override fun loadModuleInfos() = moduleInfos
override fun loadModules() = modules.values
private fun fakeModuleInfo(name: String) = ModuleInfo(name, File("/tmp/commonizer/mocks/$name"))
}