From dff392f2cd9a493b53507f61ddb2e23031b97850 Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Wed, 11 Aug 2021 16:15:03 +0200 Subject: [PATCH] [Commonizer] ClassSuperTypeCommonizer: Include transitive supertypes This commit still has some left-over todos that shall be done in a separate commit. TODOs: - CirClassifierIndex is calculated once too often - Reconsider position of classifierIndices in 'CirKnownClassifiers' - Add documentation/description to complicated 'ClassSuperTypeCommonizer' - Add documentation/description to CirSupertypesResolver ^KT-47430 Verification Pending --- .../commonizer/cir/CirSupertypesResolver.kt | 81 +++++++ .../core/ClassSuperTypeCommonizer.kt | 142 ++++++++++- .../core/TargetDependentCommonizer.kt | 22 ++ .../org/jetbrains/kotlin/commonizer/facade.kt | 2 + .../mergedtree/CirClassifierIndex.kt | 40 +++- .../CirFictitiousFunctionClassifiers.kt | 3 +- .../mergedtree/CirKnownClassifiers.kt | 1 + .../mergedtree/CirProvidedClassifiers.kt | 4 +- .../CirProvidedClassifiersByModules.kt | 18 +- .../commonized/common/package_root.kt | 2 +- .../commonizer/core/TypeCommonizerTest.kt | 1 + .../ClassSuperTypeCommonizationTest.kt | 224 ++++++++++++++++++ .../InlineTypeAliasCirNodeTransformerTest.kt | 1 + .../tree/merge/AbstractMergeCirTreeTest.kt | 1 + .../kotlin/commonizer/utils/mocks.kt | 1 + 15 files changed, 513 insertions(+), 30 deletions(-) create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirSupertypesResolver.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TargetDependentCommonizer.kt create mode 100644 native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/ClassSuperTypeCommonizationTest.kt diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirSupertypesResolver.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirSupertypesResolver.kt new file mode 100644 index 00000000000..54c481ed450 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/cir/CirSupertypesResolver.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.commonizer.cir + +import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex +import org.jetbrains.kotlin.commonizer.mergedtree.CirProvided +import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers +import org.jetbrains.kotlin.commonizer.mergedtree.findClass +import org.jetbrains.kotlin.commonizer.util.transitiveClosure +import org.jetbrains.kotlin.descriptors.Visibilities + +internal interface CirSupertypesResolver { + fun supertypes(type: CirClassType): Set +} + +internal fun CirSupertypesResolver.allSupertypes(type: CirClassType): Set { + return transitiveClosure(type, this::supertypes) +} + +internal class SimpleCirSupertypesResolver( + private val classifiers: CirClassifierIndex, + private val dependencies: CirProvidedClassifiers, +) : CirSupertypesResolver { + + override fun supertypes(type: CirClassType): Set { + classifiers.findClass(type.classifierId)?.let { classifier -> + return supertypes(type, classifier) + } + + dependencies.classifier(type.classifierId)?.let { classifier -> + if (classifier is CirProvided.Class) { + return supertypes(type, classifier) + } + } + return emptySet() + } + + private fun supertypes(type: CirClassType, classifier: CirClass): Set { + return classifier.supertypes.filterIsInstance() + .mapNotNull { superType -> createSupertype(type, superType) } + .toSet() + } + + private fun supertypes(type: CirClassType, classifier: CirProvided.Class): Set { + return classifier.supertypes.filterIsInstance() + .mapNotNull { superType -> superType.toCirClassTypeOrNull() } + .mapNotNull { superType -> createSupertype(type, superType) } + .toSet() + } + + private fun createSupertype(type: CirClassType, supertype: CirClassType): CirClassType? { + if (type.arguments.isEmpty() && supertype.arguments.isEmpty()) { + return supertype + } + + return null + } +} + +private fun CirProvided.ClassType.toCirClassTypeOrNull(): CirClassType? { + return CirClassType.createInterned( + classId = this.classId, + outerType = this.outerType?.let { it.toCirClassTypeOrNull() ?: return null }, + isMarkedNullable = this.isMarkedNullable, + arguments = this.arguments.map { it.toCirTypeProjection() ?: return null }, + visibility = Visibilities.Public, + ) +} + +private fun CirProvided.TypeProjection.toCirTypeProjection(): CirTypeProjection? { + return when (this) { + is CirProvided.StarTypeProjection -> CirStarTypeProjection + is CirProvided.RegularTypeProjection -> CirRegularTypeProjection( + projectionKind = variance, + type = (type as? CirProvided.ClassType)?.toCirClassTypeOrNull() ?: return null + ) + } +} \ No newline at end of file diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassSuperTypeCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassSuperTypeCommonizer.kt index c021415dc48..f434cdb64ae 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassSuperTypeCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/ClassSuperTypeCommonizer.kt @@ -5,27 +5,145 @@ package org.jetbrains.kotlin.commonizer.core -import org.jetbrains.kotlin.commonizer.cir.CirClassOrTypeAliasType +import org.jetbrains.kotlin.commonizer.cir.CirClassType +import org.jetbrains.kotlin.commonizer.cir.CirEntityId import org.jetbrains.kotlin.commonizer.cir.CirType +import org.jetbrains.kotlin.commonizer.cir.SimpleCirSupertypesResolver +import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers +import org.jetbrains.kotlin.commonizer.mergedtree.CirProvided +import org.jetbrains.kotlin.commonizer.mergedtree.findClass +import org.jetbrains.kotlin.commonizer.util.transitiveClosure +import org.jetbrains.kotlin.descriptors.ClassKind -class ClassSuperTypeCommonizer( +typealias Supertypes = List + +internal class ClassSuperTypeCommonizer( private val classifiers: CirKnownClassifiers -) : AssociativeCommonizer> { +) : SingleInvocationCommonizer> { private val typeCommonizer = TypeCommonizer(classifiers) - override fun commonize(first: List, second: List): List { - if (first.isEmpty() || second.isEmpty()) return emptyList() + override fun invoke(values: List): Supertypes { + if (values.isEmpty()) return emptyList() + if (values.all { it.isEmpty() }) return emptyList() - val firstGroup = first.filterIsInstance().associateBy { it.classifierId } - val secondGroup = second.filterIsInstance().associateBy { it.classifierId } + val supertypesTrees = resolveSupertypesTree(values) + val supertypesGroups = buildSupertypesGroups(supertypesTrees) - val commonClassifiers = firstGroup.keys intersect secondGroup.keys - - return commonClassifiers.mapNotNull { classifier -> - typeCommonizer.commonize(firstGroup.getValue(classifier), secondGroup.getValue(classifier)) + return supertypesGroups.mapNotNull { supertypesGroup -> + typeCommonizer.asCommonizer().commonize(supertypesGroup.types) } } -} \ No newline at end of file + private fun resolveSupertypesTree(values: List): List { + return values.mapIndexed { index: Int, supertypes: Supertypes -> + val classifierIndex = classifiers.classifierIndices[index] + val resolver = SimpleCirSupertypesResolver(classifiers.classifierIndices[index], classifiers.commonDependencies) + val nodes = supertypes.filterIsInstance().map { type -> createTypeNode(classifierIndex, resolver, type) } + SupertypesTree(nodes) + } + } + + private fun buildSupertypesGroups(trees: List): List { + val groups = mutableListOf() + var allowClassTypes = true + + trees.flatMap { tree -> tree.allNodes }.forEach { node -> + if (node.assignedGroup != null) return@forEach + val candidateGroup = buildTypeGroup(trees, node.type.classifierId) ?: return@forEach + if (containsAnyClassKind(candidateGroup)) { + if (!allowClassTypes) return@forEach + allowClassTypes = false + } + assignGroupToNodes(candidateGroup) + groups.add(candidateGroup) + } + + return groups + } + + private fun containsAnyClassKind(group: SupertypesGroup): Boolean { + return group.nodes.any { node -> isClassKind(node) } + } + + private fun isClassKind(node: TypeNode): Boolean { + if (node.index.findClass(node.type.classifierId)?.kind == ClassKind.CLASS) return true + + /* + Looking into provided dependencies. + We do not know if ExportedForwardDeclarations are always Classes, but for sake of safety, + we just assume all of those are classes. + */ + val providedClassifier = classifiers.commonDependencies.classifier(node.type.classifierId) ?: return false + return providedClassifier is CirProvided.ExportedForwardDeclarationClass || + (providedClassifier is CirProvided.RegularClass && providedClassifier.kind == ClassKind.CLASS) + } + + private fun assignGroupToNodes(group: SupertypesGroup) { + val classifiersIds = group.nodes.map { rootNode -> rootNode.allNodes.map { it.type.classifierId }.toSet() } + val coveredClassifierIds = classifiersIds.reduce { acc, list -> acc intersect list } + + group.nodes.forEach { rootNode -> + rootNode.allNodes.forEach { visitingNode -> + if (visitingNode.type.classifierId in coveredClassifierIds) { + visitingNode.assignedGroup = group + } + } + } + } + + private fun buildTypeGroup(trees: List, classifierId: CirEntityId): SupertypesGroup? { + val nodes = trees.map { otherTree: SupertypesTree -> + otherTree.allNodes.find { otherNode -> otherNode.type.classifierId == classifierId } ?: return null + } + return SupertypesGroup(classifierId, nodes) + } +} + +private fun createTypeNode(index: CirClassifierIndex, resolver: SimpleCirSupertypesResolver, type: CirClassType): TypeNode { + return TypeNode( + index = index, + type = type, + supertypes = resolver.supertypes(type).map { supertype -> createTypeNode(index, resolver, supertype) } + ) +} + +private class SupertypesGroup( + val classifierId: CirEntityId, + val nodes: List +) { + val types = nodes.map { it.type } + + init { + check(nodes.all { it.type.classifierId == classifierId }) + } +} + +private class SupertypesTree( + val nodes: List +) { + val allNodes: List = run { + val size = nodes.sumOf { it.allNodes.size } + nodes.flatMapTo(ArrayList(size)) { it.allNodes } + } +} + +private class TypeNode( + val index: CirClassifierIndex, + val type: CirClassType, + val supertypes: List, + var assignedGroup: SupertypesGroup? = null +) { + val allNodes: List by lazy { + val allSupertypes = transitiveClosure(this, TypeNode::supertypes) + ArrayList(allSupertypes.size + 1).also { list -> + list.add(this) + list.addAll(allSupertypes) + } + } + + override fun toString(): String { + return "TypeNode(${type.classifierId})" + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TargetDependentCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TargetDependentCommonizer.kt new file mode 100644 index 00000000000..1fb342fc556 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/core/TargetDependentCommonizer.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.commonizer.core + +interface SingleInvocationCommonizer { + operator fun invoke(values: List): T +} + +fun SingleInvocationCommonizer.asCommonizer(): Commonizer = object : Commonizer { + private val collectedValues = mutableListOf() + + override val result: T + get() = this@asCommonizer.invoke(collectedValues) + + override fun commonizeWith(next: T): Boolean { + collectedValues.add(next) + return true + } +} \ No newline at end of file diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt index ad392f8a0dc..e720b8422dd 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.commonizer import kotlinx.metadata.klib.ChunkedKlibModuleFragmentWriteStrategy import org.jetbrains.kotlin.commonizer.ResultsConsumer.Status import org.jetbrains.kotlin.commonizer.core.CommonizationVisitor +import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex import org.jetbrains.kotlin.commonizer.mergedtree.CirCommonizedClassifierNodes import org.jetbrains.kotlin.commonizer.mergedtree.CirKnownClassifiers import org.jetbrains.kotlin.commonizer.mergedtree.CirNode.Companion.indexOfCommon @@ -52,6 +53,7 @@ internal fun commonizeTarget( parameters.logger.progress(output, "Commonized declarations from ${inputs.targets}") { val classifiers = CirKnownClassifiers( + classifierIndices = availableTrees.toList().map(::CirClassifierIndex), // TODO NOW: Reuse for transformer!! commonizedNodes = CirCommonizedClassifierNodes.default(), commonDependencies = parameters.dependencyClassifiers(output) ) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirClassifierIndex.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirClassifierIndex.kt index 5400b3363d1..9f8e681d3a3 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirClassifierIndex.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirClassifierIndex.kt @@ -10,32 +10,55 @@ package org.jetbrains.kotlin.commonizer.mergedtree +import org.jetbrains.kotlin.commonizer.cir.CirClass +import org.jetbrains.kotlin.commonizer.cir.CirClassifier import org.jetbrains.kotlin.commonizer.cir.CirEntityId +import org.jetbrains.kotlin.commonizer.cir.CirTypeAlias import org.jetbrains.kotlin.commonizer.tree.* import org.jetbrains.kotlin.commonizer.utils.compact import org.jetbrains.kotlin.commonizer.utils.compactMapValues -internal fun CirClassifierIndex(tree: CirTreeRoot): CirClassifierIndex { - return CirUnderlyingTypeIndexBuilder().apply { invoke(tree) }.build() +fun CirClassifierIndex(tree: CirTreeRoot): CirClassifierIndex { + return CirClassifierIndexBuilder().apply { invoke(tree) }.build() } -internal interface CirClassifierIndex { +interface CirClassifierIndex { val allClassifierIds: Set + fun findClassifier(id: CirEntityId): CirClassifier? fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List } +internal fun CirClassifierIndex.findClass(id: CirEntityId): CirClass? = findClassifier(id) as? CirClass + +internal fun CirClassifierIndex.findTypeAlias(id: CirEntityId): CirTypeAlias? = findClassifier(id) as? CirTypeAlias + +internal fun CirClassifierIndex.getClass(id: CirEntityId): CirClass = + findClass(id) ?: throw NoSuchElementException("Missing class $id") + +internal fun CirClassifierIndex.getTypeAlias(id: CirEntityId): CirTypeAlias = + findTypeAlias(id) ?: throw NoSuchElementException("Missing type alias $id") + +internal fun CirClassifierIndex.getClassifier(id: CirEntityId): CirClassifier = + findClassifier(id) ?: throw NoSuchElementException("Missing classifier $id") + private class CirClassifierIndexImpl( override val allClassifierIds: Set, + private val classifiersById: Map, private val typeAliasesByUnderlyingType: Map> ) : CirClassifierIndex { + override fun findClassifier(id: CirEntityId): CirClassifier? { + return classifiersById[id] + } + override fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List { return typeAliasesByUnderlyingType[underlyingClassifier].orEmpty() } } -private class CirUnderlyingTypeIndexBuilder { - private val index = mutableMapOf>() +private class CirClassifierIndexBuilder { + private val typeAliasesByUnderlyingType = mutableMapOf>() + private val classifiersById = mutableMapOf() private val classifierIds = mutableSetOf() operator fun invoke(tree: CirTreeRoot) { @@ -53,18 +76,21 @@ private class CirUnderlyingTypeIndexBuilder { operator fun invoke(typeAlias: CirTreeTypeAlias) { classifierIds.add(typeAlias.id) - index.getOrPut(typeAlias.typeAlias.underlyingType.classifierId) { mutableListOf() }.add(typeAlias) + classifiersById[typeAlias.id] = typeAlias.typeAlias + typeAliasesByUnderlyingType.getOrPut(typeAlias.typeAlias.underlyingType.classifierId) { mutableListOf() }.add(typeAlias) } operator fun invoke(clazz: CirTreeClass) { classifierIds.add(clazz.id) + classifiersById[clazz.id] = clazz.clazz clazz.classes.forEach { innerClazz -> this(innerClazz) } } fun build(): CirClassifierIndex { return CirClassifierIndexImpl( allClassifierIds = classifierIds.toSet(), - typeAliasesByUnderlyingType = index.compactMapValues { (_, list) -> list.compact() } + classifiersById = classifiersById.compact(), + typeAliasesByUnderlyingType = typeAliasesByUnderlyingType.compactMapValues { (_, list) -> list.compact() } ) } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirFictitiousFunctionClassifiers.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirFictitiousFunctionClassifiers.kt index 32aa42dcf77..4daedc522c6 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirFictitiousFunctionClassifiers.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirFictitiousFunctionClassifiers.kt @@ -9,6 +9,7 @@ import gnu.trove.THashMap import org.jetbrains.kotlin.commonizer.cir.CirEntityId import org.jetbrains.kotlin.commonizer.cir.CirName import org.jetbrains.kotlin.commonizer.cir.CirPackageName +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.types.Variance @@ -39,7 +40,7 @@ object CirFictitiousFunctionClassifiers : CirProvidedClassifiers { } val classId = CirEntityId.create(PACKAGE_NAME, CirName.create("$prefix$arity")) - val clazz = CirProvided.RegularClass(typeParameters, emptyList(), Visibilities.Public) + val clazz = CirProvided.RegularClass(typeParameters, emptyList(), Visibilities.Public, ClassKind.INTERFACE) consumer(classId, clazz) } diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirKnownClassifiers.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirKnownClassifiers.kt index 1dac54328aa..065ab9daa97 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirKnownClassifiers.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirKnownClassifiers.kt @@ -9,6 +9,7 @@ import gnu.trove.THashMap import org.jetbrains.kotlin.commonizer.cir.CirEntityId class CirKnownClassifiers( + val classifierIndices: List, val commonizedNodes: CirCommonizedClassifierNodes, val commonDependencies: CirProvidedClassifiers ) diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirProvidedClassifiers.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirProvidedClassifiers.kt index 6ae326997ed..f0236466fcf 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirProvidedClassifiers.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirProvidedClassifiers.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.commonizer.mergedtree import org.jetbrains.kotlin.commonizer.ModulesProvider import org.jetbrains.kotlin.commonizer.cir.CirEntityId import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackages +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.types.Variance @@ -69,7 +70,8 @@ object CirProvided { data class RegularClass( override val typeParameters: List, override val supertypes: List, - override val visibility: Visibility + override val visibility: Visibility, + val kind: ClassKind ) : Class data class ExportedForwardDeclarationClass(val syntheticClassId: CirEntityId) : Class { diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirProvidedClassifiersByModules.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirProvidedClassifiersByModules.kt index 9cd536ab00c..7ee572fdbf5 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirProvidedClassifiersByModules.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirProvidedClassifiersByModules.kt @@ -12,10 +12,8 @@ import org.jetbrains.kotlin.commonizer.ModulesProvider.CInteropModuleAttributes import org.jetbrains.kotlin.commonizer.cir.CirEntityId import org.jetbrains.kotlin.commonizer.cir.CirName import org.jetbrains.kotlin.commonizer.cir.CirPackageName -import org.jetbrains.kotlin.commonizer.utils.NON_EXISTING_CLASSIFIER_ID -import org.jetbrains.kotlin.commonizer.utils.compactMap -import org.jetbrains.kotlin.commonizer.utils.compactMapIndexed -import org.jetbrains.kotlin.commonizer.utils.isUnderKotlinNativeSyntheticPackages +import org.jetbrains.kotlin.commonizer.utils.* +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.library.SerializedMetadata import org.jetbrains.kotlin.library.metadata.parsePackageFragment @@ -64,7 +62,8 @@ internal class CirProvidedClassifiersByModules private constructor( return CirProvidedClassifiersByModules(hasForwardDeclarations, classifiers) } - private val FALLBACK_FORWARD_DECLARATION_CLASS = CirProvided.RegularClass(emptyList(), emptyList(), Visibilities.Public) + private val FALLBACK_FORWARD_DECLARATION_CLASS = + CirProvided.RegularClass(emptyList(), emptyList(), Visibilities.Public, ClassKind.CLASS) } } @@ -166,11 +165,14 @@ private fun readClass( ) val typeReadContext = TypeReadContext(classEntry.strings, TypeTable(classProto.typeTable), typeParameterNameToIndex) - val supertypes = classProto.supertypeList.map { readType(it, typeReadContext) } + - classProto.supertypeIdList.map { readType(classProto.typeTable.getType(it), typeReadContext) } + val supertypes = (classProto.supertypeList.map { readType(it, typeReadContext) } + + classProto.supertypeIdList.map { readType(classProto.typeTable.getType(it), typeReadContext) }) + .filterNot { type -> type is CirProvided.ClassType && type.classId == ANY_CLASS_ID } + val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(classProto.flags)) - val clazz = CirProvided.RegularClass(typeParameters, supertypes, visibility) + val kind = ProtoEnumFlags.classKind(Flags.CLASS_KIND.get(classProto.flags)) + val clazz = CirProvided.RegularClass(typeParameters, supertypes, visibility, kind) consumer(classId, clazz) diff --git a/native/commonizer/testData/classifierCommonization/supertypes/commonized/common/package_root.kt b/native/commonizer/testData/classifierCommonization/supertypes/commonized/common/package_root.kt index 8628310459f..f9886bfc36d 100644 --- a/native/commonizer/testData/classifierCommonization/supertypes/commonized/common/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/supertypes/commonized/common/package_root.kt @@ -8,7 +8,7 @@ expect abstract class A2() : A1 { abstract fun function2(): Int } -expect class A3() : A2 { +expect class A3() : A2, A1 { override val property1: Int override val property2: Int val property3: Int diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt index 0e724d22f4e..890ecb7b904 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/core/TypeCommonizerTest.kt @@ -25,6 +25,7 @@ class TypeCommonizerTest : AbstractCommonizerTest() { fun initialize() { // reset cache classifiers = CirKnownClassifiers( + classifierIndices = emptyList(), // TODO NOW commonizedNodes = CirCommonizedClassifierNodes.default(), commonDependencies = object : CirProvidedClassifiers { override fun hasClassifier(classifierId: CirEntityId) = classifierId.packageName.isUnderStandardKotlinPackages diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/ClassSuperTypeCommonizationTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/ClassSuperTypeCommonizationTest.kt new file mode 100644 index 00000000000..23badbc36bc --- /dev/null +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/hierarchical/ClassSuperTypeCommonizationTest.kt @@ -0,0 +1,224 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.commonizer.hierarchical + +import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest +import org.jetbrains.kotlin.commonizer.assertCommonized +import org.junit.Test + +class ClassSuperTypeCommonizationTest : AbstractInlineSourcesCommonizationTest() { + + @Test + fun `test common supertype - from dependencies`() { + val result = commonize { + outputTarget("(a, b)") + registerDependency("a", "b", "(a, b)") { + source( + """ + interface A + interface B: A + """.trimIndent() + ) + } + + simpleSingleSourceTarget( + "a", """ + class X: A + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + class X: B + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b)", """ + expect class X(): A + """.trimIndent() + ) + } + + @Test + fun `test common supertype - from sources`() { + val result = commonize { + outputTarget("(a, b)") + + simpleSingleSourceTarget( + "a", """ + interface A + interface B: A + class X: A + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + interface A + interface B: A + class X: B + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b)", """ + expect interface A + expect interface B: A + expect class X(): A + """.trimIndent() + ) + } + + @Test + fun `test common supertype - from sources - missing interface in one target`() { + val result = commonize { + outputTarget("(a, b)") + + simpleSingleSourceTarget( + "a", """ + interface A + class X: A + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + interface A + interface B: A + class X: B + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b)", """ + expect interface A + expect class X(): A + """.trimIndent() + ) + } + + @Test + fun `test common supertypes - sample 0`() { + val result = commonize { + outputTarget("(a, b)") + + simpleSingleSourceTarget( + "a", """ + interface C + interface B + interface A: B, C + + class X: A + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + interface C + interface B: C + interface A + + class X: A, B + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b)", """ + expect interface C + expect interface B + expect interface A + + expect class X(): A, B, C + """.trimIndent() + ) + } + + @Test + fun `test common supertypes - sample 1`() { + val result = commonize { + outputTarget("(a, b)") + + simpleSingleSourceTarget( + "a", """ + interface C + open class B + open class A: C, B() + + class X: A() + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + interface C + interface B: C + open class A + + class X: A(), B + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b)", """ + expect interface C + expect open class A() + expect class X(): A, C + """.trimIndent() + ) + } + + @Test + fun `test common supertypes - sample 2`() { + val result = commonize { + outputTarget("(a, b, c)") + simpleSingleSourceTarget( + "a", """ + interface C + interface B + interface A: B, C + + class X: A + """.trimIndent() + ) + + simpleSingleSourceTarget( + "b", """ + interface C + interface B + interface A: B + + class X: A, C + """.trimIndent() + ) + + simpleSingleSourceTarget( + "c", """ + interface C + interface B: C + interface A + + class X: A, B + """.trimIndent() + ) + } + + result.assertCommonized( + "(a, b, c)", """ + expect interface C + expect interface B + expect interface A + + expect class X(): A, B, C + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/transformer/InlineTypeAliasCirNodeTransformerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/transformer/InlineTypeAliasCirNodeTransformerTest.kt index 8264b859c25..d9174122f89 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/transformer/InlineTypeAliasCirNodeTransformerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/transformer/InlineTypeAliasCirNodeTransformerTest.kt @@ -15,6 +15,7 @@ class InlineTypeAliasCirNodeTransformerTest { private val storageManager = LockBasedStorageManager("test") private val classifiers = CirKnownClassifiers( + classifierIndices = emptyList(), commonizedNodes = CirCommonizedClassifierNodes.default(), commonDependencies = CirProvidedClassifiers.EMPTY ) diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/AbstractMergeCirTreeTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/AbstractMergeCirTreeTest.kt index aeb7f431ec9..e9d187c34b9 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/AbstractMergeCirTreeTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/tree/merge/AbstractMergeCirTreeTest.kt @@ -88,6 +88,7 @@ abstract class AbstractMergeCirTreeTest : KtInlineSourceCommonizerTestCase() { private fun createDefaultKnownClassifiers(): CirKnownClassifiers { return CirKnownClassifiers( + emptyList(), // TODO NOW CirCommonizedClassifierNodes.default(), CirProvidedClassifiers.of( CirFictitiousFunctionClassifiers, diff --git a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/mocks.kt b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/mocks.kt index 913a3fcc39c..a6e7cdfc206 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/mocks.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/commonizer/utils/mocks.kt @@ -52,6 +52,7 @@ private fun createValidClassifierId(classifierId: String): CirEntityId { } internal val MOCK_CLASSIFIERS = CirKnownClassifiers( + classifierIndices = emptyList(), commonizedNodes = object : CirCommonizedClassifierNodes { override fun classNode(classId: CirEntityId) = CirClassNode( classId,