[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
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import java.io.File
sealed class Result
sealed class Result {
object NothingToCommonize : Result()
object NothingToCommonize : Result()
class CommonizationPerformed(
val modulesByTargets: Map<Target, Collection<ModuleDescriptor>>
) : Result() {
val commonTarget: OutputTarget by lazy {
modulesByTargets.keys.filterIsInstance<OutputTarget>().single()
}
val concreteTargets: Set<InputTarget> by lazy {
modulesByTargets.keys.filterIsInstance<InputTarget>().toSet()
class Commonized(
val modulesByTargets: Map<Target, Collection<ModuleResult>>
) : Result() {
val sharedTarget: OutputTarget by lazy { modulesByTargets.keys.filterIsInstance<OutputTarget>().single() }
val leafTargets: 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
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import java.io.File
class TargetProvider(
val target: InputTarget,
@@ -19,20 +21,15 @@ interface BuiltInsProvider {
fun loadBuiltIns(): KotlinBuiltIns
companion object {
fun wrap(builtIns: KotlinBuiltIns) = object : BuiltInsProvider {
override fun loadBuiltIns() = builtIns
val defaultBuiltInsProvider = object : BuiltInsProvider {
override fun loadBuiltIns() = DefaultBuiltIns.Instance
}
}
}
interface ModulesProvider {
class ModuleInfo(val name: String, val originalLocation: File)
fun loadModuleInfos(): Map<String, ModuleInfo>
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.utils.CommonizedGroup
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.utils.addIfNotNull
/**
* Serves two goals:
@@ -28,16 +27,17 @@ internal class DeclarationsBuilderVisitor1(
check(data.isEmpty()) // root node may not have containing declarations
check(components.targetComponents.size == node.dimension)
val allTargets = (node.targetDeclarations + node.commonDeclaration()).map { it!!.target }
val modulesByTargets = HashMap<Target, MutableList<ModuleDescriptorImpl>>()
val allTargets: List<Target> = (node.targetDeclarations + node.commonDeclaration()).map { it!!.target }
val modulesByTargets: Map<Target, MutableList<ModuleDescriptorImpl>> = allTargets.associateWithTo(HashMap()) { mutableListOf() }
// collect module descriptors:
for (moduleNode in node.modules.values) {
val modules = moduleNode.accept(this, noContainingDeclarations()).asListContaining<ModuleDescriptorImpl>()
modules.forEachIndexed { index, module ->
val target = allTargets[index]
modulesByTargets.getOrPut(target) { mutableListOf() }.addIfNotNull(module)
if (module != null) {
val target = allTargets[index]
modulesByTargets.getValue(target) += module
}
}
}
@@ -50,9 +50,9 @@ private fun parseTask(
fun buildOngoingTask() {
// check options completeness
val missedMandatoryOptions = optionTypes.filterKeys { it !in options }.filterValues { it.mandatory }.keys
if (missedMandatoryOptions.isNotEmpty())
printUsageAndExit("Mandatory options not specified in task $taskAlias: " + missedMandatoryOptions.joinToString { "-$it" })
val missingMandatoryOptions = optionTypes.filterKeys { it !in options }.filterValues { it.mandatory }.keys
if (missingMandatoryOptions.isNotEmpty())
printUsageAndExit("Mandatory options not specified in task $taskAlias: " + missingMandatoryOptions.joinToString { "-$it" })
tasks += taskType.taskConstructor(options.values)
}
@@ -20,7 +20,7 @@ class RootCommonizer : AbstractStandardCommonizer<CirRoot, CirRoot>() {
override fun commonizationResult() = CirRootFactory.create(
target = OutputTarget(inputTargets),
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) {
@@ -5,7 +5,6 @@
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.DeclarationsBuilderVisitor2
import org.jetbrains.kotlin.descriptors.commonizer.builder.createGlobalBuilderComponents
@@ -15,14 +14,15 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager
fun runCommonization(parameters: Parameters): Result {
if (!parameters.hasAnythingToCommonize())
return NothingToCommonize
return Result.NothingToCommonize
val storageManager = LockBasedStorageManager("Declaration descriptors commonization")
// build merged tree:
val mergedTree = CirTreeMerger(storageManager, parameters).merge()
val mergeResult = CirTreeMerger(storageManager, parameters).merge()
// commonize:
val mergedTree = mergeResult.root
mergedTree.accept(CommonizationVisitor(mergedTree), Unit)
parameters.progressLogger?.invoke("Commonized declarations")
@@ -31,15 +31,21 @@ fun runCommonization(parameters: Parameters): Result {
mergedTree.accept(DeclarationsBuilderVisitor1(components), emptyList())
mergedTree.accept(DeclarationsBuilderVisitor2(components), emptyList())
val modulesByTargets = LinkedHashMap<Target, Collection<ModuleDescriptor>>() // use linked hash map to preserve order
components.targetComponents.forEach {
val target = it.target
val modulesByTargets = LinkedHashMap<Target, Collection<ModuleResult>>() // use linked hash map to preserve order
components.targetComponents.forEach { component ->
val target = component.target
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")
return CommonizationPerformed(modulesByTargets)
return Result.Commonized(modulesByTargets)
}
@@ -91,9 +91,9 @@ class NativeDistributionCommonizer(
val stdlib = loadLibrary(stdlibPath)
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 }
?.listFiles()
?.takeIf { it.isNotEmpty() }
@@ -103,7 +103,7 @@ class NativeDistributionCommonizer(
if (platformLibs.isEmpty())
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")
@@ -176,16 +176,16 @@ class NativeDistributionCommonizer(
copyCommonStandardLibraries()
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.
// 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) ->
copyTargetAsIs(target, libraries.platformLibs.size)
}
}
is CommonizationPerformed -> {
is Result.Commonized -> {
val serializer = KlibMetadataMonolithicSerializer(
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
metadataVersion = KlibMetadataVersion.INSTANCE,
@@ -193,7 +193,7 @@ class NativeDistributionCommonizer(
)
// '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()) {
targetsToCopy.forEach { target ->
val libraries = originalLibrariesByTargets.getValue(target)
@@ -201,11 +201,13 @@ class NativeDistributionCommonizer(
}
}
val concreteTargetNames = result.concreteTargets.map { it.name }
val targetsToSerialize = result.concreteTargets + result.commonTarget
val leafTargetNames = result.leafTargets.map { it.name }
val targetsToSerialize = result.leafTargets + result.sharedTarget
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 starredTarget: String?
@@ -220,8 +222,8 @@ class NativeDistributionCommonizer(
}
}
val targetName = concreteTargetNames.joinToString { if (it == starredTarget) "$it(*)" else it }
serializeTarget(target, targetName, newModules, manifestProvider, serializer)
val targetName = leafTargetNames.joinToString { if (it == starredTarget) "$it(*)" else it }
serializeTarget(target, targetName, newModules, absentModuleLocations, manifestProvider, serializer)
}
}
}
@@ -252,24 +254,26 @@ class NativeDistributionCommonizer(
}
}
private fun copyTargetAsIs(target: InputTarget, platformLibrariesCount: Int) {
val librariesDestination = target.librariesDestination
private fun copyTargetAsIs(leafTarget: InputTarget, librariesCount: Int) {
val librariesDestination = leafTarget.librariesDestination
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)
logProgress("Copied $platformLibrariesCount libraries for [${target.name}]")
logProgress("Copied $librariesCount libraries for [${leafTarget.name}]")
}
private fun serializeTarget(
target: Target,
targetName: String,
newModules: Collection<ModuleDescriptor>,
absentModuleLocations: List<File>,
manifestProvider: NativeManifestDataProvider,
serializer: KlibMetadataMonolithicSerializer
) {
val librariesDestination = target.librariesDestination
librariesDestination.mkdirs() // always create an empty directory even if there is nothing to copy
for (newModule in newModules) {
val libraryName = newModule.name
@@ -286,6 +290,11 @@ class NativeDistributionCommonizer(
writeLibrary(metadata, manifestData, libraryDestination)
}
for (absentModuleLocation in absentModuleLocations) {
val libraryName = absentModuleLocation.name
absentModuleLocation.copyRecursively(librariesDestination.resolve(libraryName))
}
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.commonizer.BuiltInsProvider
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.createKotlinNativeForwardDeclarationsModule
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.storage.StorageManager
import java.io.File
internal class NativeDistributionModulesProvider(
private val storageManager: StorageManager,
@@ -32,6 +34,15 @@ internal class NativeDistributionModulesProvider(
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> {
val builtIns = loadBuiltIns()
val stdlib = builtIns.builtInsModule
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.descriptors.commonizer.mergedtree
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.TargetProvider
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirClass
@@ -23,26 +25,44 @@ class CirTreeMerger(
private val storageManager: StorageManager,
private val parameters: Parameters
) {
class CirTreeMergeResult(
val root: CirRootNode,
val absentModuleInfos: Map<InputTarget, Collection<ModuleInfo>>
)
private val size = parameters.targetProviders.size
private lateinit var cacheRW: CirClassifiersCacheImpl
fun merge(): CirRootNode {
fun merge(): CirTreeMergeResult {
val rootNode: CirRootNode = buildRootNode(storageManager, size)
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 ->
processTarget(rootNode, targetIndex, targetProvider)
processTarget(rootNode, targetIndex, targetProvider, commonModuleNames)
parameters.progressLogger?.invoke("Loaded declarations for [${targetProvider.target.name}]")
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(
rootNode: CirRootNode,
targetIndex: Int,
targetProvider: TargetProvider
targetProvider: TargetProvider,
commonModuleNames: Set<String>
) {
rootNode.targetDeclarations[targetIndex] = CirRootFactory.create(
targetProvider.target,
@@ -54,7 +74,8 @@ class CirTreeMerger(
val modules: MutableMap<Name, CirModuleNode> = rootNode.modules
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, %%",
"Commonized",
"Commonized, %%",
"Missed in s. targets",
"Missed in s. targets, %%",
"Missing in s. targets",
"Missing in s. targets, %%",
"Failed: Other",
"Failed: Other, %%",
"Total"
@@ -19,10 +19,8 @@ import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
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.assertIsDirectory
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertModulesAreEqual
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertValidModule
import org.jetbrains.kotlin.descriptors.commonizer.utils.*
import org.jetbrains.kotlin.descriptors.commonizer.utils.MockBuiltInsProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.CommonPlatforms
@@ -42,10 +40,6 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
init {
System.setProperty("java.awt.headless", "true")
}
fun Collection<ModuleDescriptor>.eachModuleAsTarget() = mapIndexed { index, moduleDescriptor ->
InputTarget("target_$index") to moduleDescriptor
}.toMap().toCommonizationParameters()
}
private fun getTestDataDir(): File {
@@ -69,26 +63,28 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
val result: Result = runCommonization(analyzedModules.toCommonizationParameters())
assertCommonizationPerformed(result)
val commonTarget: OutputTarget = analyzedModules.commonizedCommonModule.target
assertEquals(commonTarget, result.commonTarget)
val sharedTarget: OutputTarget = analyzedModules.commonizedCommonModule.target
assertEquals(sharedTarget, result.sharedTarget)
val commonModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedCommonModule.module
val commonModuleByCommonizer: ModuleDescriptor = result.modulesByTargets.getValue(commonTarget).single()
val sharedModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedCommonModule.module
val sharedModuleByCommonizer: ModuleDescriptor =
(result.modulesByTargets.getValue(sharedTarget).single() as ModuleResult.Commonized).module
assertValidModule(commonModuleAsExpected)
assertValidModule(commonModuleByCommonizer)
assertModulesAreEqual(commonModuleAsExpected, commonModuleByCommonizer, "\"$commonTarget\" target")
assertValidModule(sharedModuleAsExpected)
assertValidModule(sharedModuleByCommonizer)
assertModulesAreEqual(sharedModuleAsExpected, sharedModuleByCommonizer, "\"$sharedTarget\" target")
val concreteTargets: Set<InputTarget> = analyzedModules.commonizedPlatformModules.keys
assertEquals(concreteTargets, result.concreteTargets)
val leafTargets: Set<InputTarget> = analyzedModules.commonizedPlatformModules.keys
assertEquals(leafTargets, result.leafTargets)
for (target in concreteTargets) {
val targetModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedPlatformModules.getValue(target).module
val targetModuleByCommonizer: ModuleDescriptor = result.modulesByTargets.getValue(target).single()
for (leafTarget in leafTargets) {
val leafTargetModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedPlatformModules.getValue(leafTarget).module
val leafTargetModuleByCommonizer: ModuleDescriptor =
(result.modulesByTargets.getValue(leafTarget).single() as ModuleResult.Commonized).module
assertValidModule(targetModuleAsExpected)
assertValidModule(targetModuleByCommonizer)
assertModulesAreEqual(targetModuleAsExpected, targetModuleByCommonizer, "\"$target\" target")
assertValidModule(leafTargetModuleAsExpected)
assertValidModule(leafTargetModuleByCommonizer)
assertModulesAreEqual(leafTargetModuleAsExpected, leafTargetModuleByCommonizer, "\"$leafTarget\" target")
}
}
}
@@ -327,8 +323,8 @@ private fun Map<InputTarget, ModuleDescriptor>.toCommonizationParameters(): Para
TargetProvider(
target = target,
builtInsClass = moduleDescriptor.builtIns::class.java,
builtInsProvider = BuiltInsProvider.wrap(moduleDescriptor.builtIns),
modulesProvider = ModulesProvider.wrap(moduleDescriptor)
builtInsProvider = MockBuiltInsProvider(moduleDescriptor.builtIns),
modulesProvider = MockModulesProvider(moduleDescriptor)
)
)
}
@@ -5,10 +5,9 @@
package org.jetbrains.kotlin.descriptors.commonizer
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.commonizer.AbstractCommonizationFromSourcesTest.Companion.eachModuleAsTarget
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.descriptors.commonizer.utils.MockModulesProvider
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertCommonizationPerformed
import org.jetbrains.kotlin.descriptors.commonizer.utils.mockEmptyModule
import org.junit.Test
import kotlin.contracts.ExperimentalContracts
import kotlin.test.assertEquals
@@ -17,49 +16,120 @@ import kotlin.test.assertEquals
class CommonizerFacadeTest {
@Test
fun nothingToCommonize0() {
val modules = listOf<ModuleDescriptor>()
val result = runCommonization(modules.eachModuleAsTarget())
assertEquals(NothingToCommonize, result)
}
fun nothingToCommonize0() = doTestNothingToCommonize(
emptyMap()
)
@Test
fun nothingToCommonize1() {
val modules = listOf(
mockEmptyModule("<foo>")
fun nothingToCommonize1() = doTestNothingToCommonize(
mapOf(
"target1" to listOf("foo")
)
val result = runCommonization(modules.eachModuleAsTarget())
assertEquals(NothingToCommonize, result)
}
)
@Test
fun commonized() {
val modules = listOf(
mockEmptyModule("<foo>"),
mockEmptyModule("<foo>")
fun commonized1() = doTestSuccessfulCommonization(
mapOf(
"target1" to listOf("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)
for (target in result.concreteTargets) {
assertSingleModuleForTarget("<foo>", result.modulesByTargets.getValue(target))
companion object {
private fun Map<String, List<String>>.toCommonizationParameters() = Parameters().also {
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.jvm.JvmBuiltIns
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.OutputTarget
import org.jetbrains.kotlin.descriptors.commonizer.Target
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirRoot
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.storage.LockBasedStorageManager
import org.junit.Test
@@ -167,7 +167,7 @@ class RootCommonizerTest : AbstractCommonizerTest<CirRoot, CirRoot>() {
fun KotlinBuiltIns.toMock(target: Target) = CirRootFactory.create(
target = target,
builtInsClass = this::class.java.name,
builtInsProvider = BuiltInsProvider.wrap(this)
builtInsProvider = MockBuiltInsProvider(this)
)
}
}
@@ -6,9 +6,7 @@
package org.jetbrains.kotlin.descriptors.commonizer.utils
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.commonizer.CommonizationPerformed
import org.jetbrains.kotlin.descriptors.commonizer.Result
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.test.util.DescriptorValidator.*
import org.jetbrains.kotlin.types.ErrorUtils
@@ -26,11 +24,11 @@ fun assertIsDirectory(file: File) {
@ExperimentalContracts
fun assertCommonizationPerformed(result: Result) {
contract {
returns() implies (result is CommonizationPerformed)
returns() implies (result is Result.Commonized)
}
if (result !is CommonizationPerformed)
fail("$result is not instance of ${CommonizationPerformed::class}")
if (result !is Result.Commonized)
fail("$result is not instance of ${Result.Commonized::class}")
}
@ExperimentalContracts
@@ -6,9 +6,13 @@
package org.jetbrains.kotlin.descriptors.commonizer.utils
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
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.ModulesProvider
import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider.ModuleInfo
import org.jetbrains.kotlin.descriptors.commonizer.builder.*
import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirClassNode
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.test.KotlinTestUtils
import org.jetbrains.kotlin.types.*
import java.io.File
import kotlin.random.Random
// 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 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"))
}