From 096e715652eb12ec9a8075b52075b1d2cb868ae3 Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Mon, 22 Mar 2021 13:44:45 +0100 Subject: [PATCH] [Commonizer] Split CirTreeMerger into composable 'Mergers' --- .../org/jetbrains/kotlin/commonizer/facade.kt | 6 +- .../commonizer/mergedtree/CirTreeMerger.kt | 398 +++--------------- .../mergers/ClassConstructorMerger.kt | 30 ++ .../mergedtree/mergers/ClassMerger.kt | 80 ++++ .../mergedtree/mergers/ClassesToProcess.kt | 69 +++ .../mergedtree/mergers/FunctionMerger.kt | 43 ++ .../mergedtree/mergers/ModuleMerger.kt | 42 ++ .../mergedtree/mergers/PackageMerger.kt | 58 +++ .../mergedtree/mergers/PropertyMerger.kt | 37 ++ .../mergedtree/mergers/RootMerger.kt | 58 +++ .../mergedtree/mergers/TargetMerger.kt | 37 ++ .../mergedtree/mergers/TypeAliasMerger.kt | 35 ++ 12 files changed, 542 insertions(+), 351 deletions(-) create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassConstructorMerger.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassMerger.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassesToProcess.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/FunctionMerger.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ModuleMerger.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/PackageMerger.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/PropertyMerger.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/RootMerger.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/TargetMerger.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/TypeAliasMerger.kt diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt index 66d84b1a23d..99a631e0600 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/facade.kt @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.commonizer.ResultsConsumer.Status import org.jetbrains.kotlin.commonizer.core.CommonizationVisitor import org.jetbrains.kotlin.commonizer.mergedtree.* import org.jetbrains.kotlin.commonizer.mergedtree.CirNode.Companion.dimension -import org.jetbrains.kotlin.commonizer.mergedtree.CirTreeMerger.CirTreeMergeResult import org.jetbrains.kotlin.commonizer.metadata.CirTreeSerializer import org.jetbrains.kotlin.library.SerializedMetadata import org.jetbrains.kotlin.storage.LockBasedStorageManager @@ -45,11 +44,10 @@ private fun mergeAndCommonize(storageManager: StorageManager, parameters: Common CirProvidedClassifiers.by(parameters.dependencyModulesProvider) ) ) - val mergeResult = CirTreeMerger(storageManager, classifiers, parameters).build() + val mergeResult = mergeCirTree(storageManager, classifiers, parameters) // commonize: - val mergedTree = mergeResult.root - mergedTree.accept(CommonizationVisitor(classifiers, mergedTree), Unit) + mergeResult.root.accept(CommonizationVisitor(classifiers, mergeResult.root), Unit) parameters.progressLogger?.invoke("Commonized declarations") return mergeResult diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirTreeMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirTreeMerger.kt index 7adb98a9f24..274105a3522 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirTreeMerger.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/CirTreeMerger.kt @@ -5,22 +5,17 @@ package org.jetbrains.kotlin.commonizer.mergedtree -import com.intellij.util.containers.FactoryMap -import kotlinx.metadata.* -import kotlinx.metadata.klib.KlibModuleMetadata -import kotlinx.metadata.klib.fqName -import kotlinx.metadata.klib.klibEnumEntries +import kotlinx.metadata.KmTypeParameter import org.jetbrains.kotlin.commonizer.CommonizerParameters import org.jetbrains.kotlin.commonizer.LeafCommonizerTarget import org.jetbrains.kotlin.commonizer.ModulesProvider.ModuleInfo -import org.jetbrains.kotlin.commonizer.TargetProvider -import org.jetbrains.kotlin.commonizer.cir.* -import org.jetbrains.kotlin.commonizer.mergedtree.ClassesToProcess.ClassEntry -import org.jetbrains.kotlin.commonizer.metadata.CirDeserializers +import org.jetbrains.kotlin.commonizer.mergedtree.mergers.* +import org.jetbrains.kotlin.commonizer.mergedtree.mergers.ClassesToProcess +import org.jetbrains.kotlin.commonizer.mergedtree.mergers.ModuleMerger +import org.jetbrains.kotlin.commonizer.mergedtree.mergers.PropertyMerger +import org.jetbrains.kotlin.commonizer.mergedtree.mergers.RootMerger +import org.jetbrains.kotlin.commonizer.mergedtree.mergers.TargetMerger import org.jetbrains.kotlin.commonizer.metadata.CirTypeResolver -import org.jetbrains.kotlin.commonizer.metadata.utils.SerializedMetadataLibraryProvider -import org.jetbrains.kotlin.commonizer.prettyName -import org.jetbrains.kotlin.commonizer.utils.* import org.jetbrains.kotlin.storage.StorageManager /** @@ -48,347 +43,56 @@ import org.jetbrains.kotlin.storage.StorageManager * targets (which is not supported yet), or a Native library where one fragment is produced via C-interop tool and the other one * is compiled from Kotlin/Native source code (not sure this should be supported at all). */ -class CirTreeMerger( - private val storageManager: StorageManager, - private val classifiers: CirKnownClassifiers, - private val parameters: CommonizerParameters + +class CirTreeMergeResult( + val root: CirRootNode, + val missingModuleInfos: Map> +) + +internal class CirTargetMergingContext( + val storageManager: StorageManager, + val classifiers: CirKnownClassifiers, + val parameters: CommonizerParameters, + val targetIndex: Int, + val typeResolver: CirTypeResolver ) { - class CirTreeMergeResult( - val root: CirRootNode, - val missingModuleInfos: Map> - ) + val targets = parameters.targetProviders.size +} - private class CirTreeMergingContext( - val targetIndex: Int, - val typeResolver: CirTypeResolver - ) { - fun create(classEntry: ClassEntry): CirTreeMergingContext = when (classEntry) { - is ClassEntry.RegularClassEntry -> create(classEntry.clazz.typeParameters) - is ClassEntry.EnumEntry -> this - } +internal fun CirTargetMergingContext.create(classEntry: ClassesToProcess.ClassEntry): CirTargetMergingContext = when (classEntry) { + is ClassesToProcess.ClassEntry.RegularClassEntry -> create(classEntry.clazz.typeParameters) + is ClassesToProcess.ClassEntry.EnumEntry -> this +} - fun create(typeParameters: List): CirTreeMergingContext { - val newTypeResolver = typeResolver.create(typeParameters) - return if (newTypeResolver !== typeResolver) - CirTreeMergingContext(targetIndex, newTypeResolver) - else - this - } - } +internal fun CirTargetMergingContext.create(typeParameters: List): CirTargetMergingContext { + val newTypeResolver = typeResolver.create(typeParameters) + return if (newTypeResolver !== typeResolver) + CirTargetMergingContext(storageManager, classifiers, parameters, targetIndex, newTypeResolver) + else + this +} - private val leafTargetsSize = parameters.targetProviders.size - - fun build(): CirTreeMergeResult { - val result = processRoot() - System.gc() - return result - } - - private fun processRoot(): CirTreeMergeResult { - val rootNode: CirRootNode = buildRootNode(storageManager, leafTargetsSize) - - val commonModuleNames = parameters.getCommonModuleNames() - val missingModuleInfosByTargets = mutableMapOf>() - - parameters.targetProviders.forEachIndexed { targetIndex, targetProvider -> - val allModuleInfos = targetProvider.modulesProvider.loadModuleInfos() - - val (commonModuleInfos, missingModuleInfos) = allModuleInfos.partition { it.name in commonModuleNames } - processTarget(targetIndex, rootNode, targetProvider, commonModuleInfos) - - missingModuleInfosByTargets[targetProvider.target] = missingModuleInfos - - parameters.progressLogger?.invoke("Loaded declarations for ${targetProvider.target.prettyName}") - System.gc() - } - - return CirTreeMergeResult( - root = rootNode, - missingModuleInfos = missingModuleInfosByTargets - ) - } - - private fun processTarget( - targetIndex: Int, - rootNode: CirRootNode, - targetProvider: TargetProvider, - commonModuleInfos: Collection - ) { - rootNode.targetDeclarations[targetIndex] = CirRoot.create(targetProvider.target) - - if (commonModuleInfos.isEmpty()) - return - - val context = CirTreeMergingContext( - targetIndex = targetIndex, - // all classifiers "visible" for the target: - typeResolver = CirTypeResolver.create( - providedClassifiers = CirProvidedClassifiers.of( - classifiers.commonDependencies, - CirProvidedClassifiers.by(targetProvider.dependencyModulesProvider), - CirProvidedClassifiers.by(targetProvider.modulesProvider) +fun mergeCirTree( + storageManager: StorageManager, + classifiers: CirKnownClassifiers, + parameters: CommonizerParameters +): CirTreeMergeResult { + val rootMerger = RootMerger( + targetMerger = TargetMerger( + moduleMerger = ModuleMerger( + packageMerger = PackageMerger( + propertyMerger = PropertyMerger, + functionMerger = FunctionMerger, + classMerger = ClassMerger( + classConstructorMerger = ClassConstructorMerger, + propertyMerger = PropertyMerger, + functionMerger = FunctionMerger, + ), + typeAliasMerger = TypeAliasMerger ) ) ) + ) - commonModuleInfos.forEach { moduleInfo -> - val metadata = targetProvider.modulesProvider.loadModuleMetadata(moduleInfo.name) - val module = KlibModuleMetadata.read(SerializedMetadataLibraryProvider(metadata)) - processModule(context, rootNode, module) - } - } - - private fun processModule( - context: CirTreeMergingContext, - rootNode: CirRootNode, - module: KlibModuleMetadata - ) { - val moduleName: CirName = CirName.create(module.name) - val moduleNode: CirModuleNode = rootNode.modules.getOrPut(moduleName) { - buildModuleNode(storageManager, leafTargetsSize) - } - moduleNode.targetDeclarations[context.targetIndex] = CirModule.create(moduleName) - - val groupedFragments: Map> = module.fragments.foldToMap { fragment -> - fragment.fqName?.let(CirPackageName::create) ?: error("A fragment without FQ name in module $moduleName: $fragment") - } - - groupedFragments.forEach { (packageName, fragments) -> - processFragments(context, moduleNode, fragments, packageName) - } - } - - private fun processFragments( - context: CirTreeMergingContext, - moduleNode: CirModuleNode, - fragments: Collection, - packageName: CirPackageName - ) { - val packageNode: CirPackageNode = moduleNode.packages.getOrPut(packageName) { - buildPackageNode(storageManager, leafTargetsSize) - } - packageNode.targetDeclarations[context.targetIndex] = CirPackage.create(packageName) - - val classesToProcess = ClassesToProcess() - fragments.forEach { fragment -> - classesToProcess.addClassesFromFragment(fragment) - - fragment.pkg?.let { pkg -> - pkg.properties.forEach { property -> - val propertyContext = context.create(property.typeParameters) - processProperty(propertyContext, packageNode, property) - } - pkg.functions.forEach { function -> - val functionContext = context.create(function.typeParameters) - processFunction(functionContext, packageNode, function) - } - pkg.typeAliases.forEach { typeAlias -> - val typeAliasContext = context.create(typeAlias.typeParameters) - processTypeAlias(typeAliasContext, packageNode, typeAlias) - } - } - } - - classesToProcess.forEachClassInScope(parentClassId = null) { classEntry -> - val classContext = context.create(classEntry) - processClass(classContext, packageNode, classEntry, classesToProcess) - } - } - - private fun processProperty( - context: CirTreeMergingContext, - ownerNode: CirNodeWithMembers<*, *>, - property: KmProperty - ) { - if (property.isFakeOverride()) - return - - val maybeClassOwnerNode: CirClassNode? = ownerNode as? CirClassNode - - val approximationKey = PropertyApproximationKey(property, context.typeResolver) - val propertyNode: CirPropertyNode = ownerNode.properties.getOrPut(approximationKey) { - buildPropertyNode(storageManager, leafTargetsSize, classifiers, maybeClassOwnerNode?.commonDeclaration) - } - propertyNode.targetDeclarations[context.targetIndex] = CirDeserializers.property( - name = approximationKey.name, - source = property, - containingClass = maybeClassOwnerNode?.targetDeclarations?.get(context.targetIndex), - typeResolver = context.typeResolver - ) - } - - private fun processFunction( - context: CirTreeMergingContext, - ownerNode: CirNodeWithMembers<*, *>, - function: KmFunction - ) { - if (function.isFakeOverride() - || function.isKniBridgeFunction() - || function.isTopLevelDeprecatedFunction(isTopLevel = ownerNode !is CirClassNode) - ) { - return - } - - val maybeClassOwnerNode: CirClassNode? = ownerNode as? CirClassNode - - val approximationKey = FunctionApproximationKey(function, context.typeResolver) - val functionNode: CirFunctionNode = ownerNode.functions.getOrPut(approximationKey) { - buildFunctionNode(storageManager, leafTargetsSize, classifiers, maybeClassOwnerNode?.commonDeclaration) - } - functionNode.targetDeclarations[context.targetIndex] = CirDeserializers.function( - name = approximationKey.name, - source = function, - containingClass = maybeClassOwnerNode?.targetDeclarations?.get(context.targetIndex), - typeResolver = context.typeResolver - ) - } - - private fun processClass( - context: CirTreeMergingContext, - ownerNode: CirNodeWithMembers<*, *>, - classEntry: ClassEntry, - classesToProcess: ClassesToProcess - ) { - val classId = classEntry.classId - val className = classId.relativeNameSegments.last() - - val maybeClassOwnerNode: CirClassNode? = ownerNode as? CirClassNode - val classNode: CirClassNode = ownerNode.classes.getOrPut(className) { - buildClassNode(storageManager, leafTargetsSize, classifiers, maybeClassOwnerNode?.commonDeclaration, classId) - } - - val clazz: KmClass? - val isEnumEntry: Boolean - - classNode.targetDeclarations[context.targetIndex] = when (classEntry) { - is ClassEntry.RegularClassEntry -> { - clazz = classEntry.clazz - isEnumEntry = Flag.Class.IS_ENUM_ENTRY(clazz.flags) - - CirDeserializers.clazz(className, clazz, context.typeResolver) - } - is ClassEntry.EnumEntry -> { - clazz = null - isEnumEntry = true - - CirDeserializers.defaultEnumEntry( - name = className, - annotations = classEntry.annotations, - enumClassId = classEntry.enumClassId, - enumClass = classEntry.enumClass, - typeResolver = context.typeResolver - ) - } - } - - if (!isEnumEntry) { - clazz?.constructors?.forEach { constructor -> - // TODO: nowhere to read constructor type parameters from - //val constructorContext = context.create(constructor.typeParameters) - - processClassConstructor(context, classNode, constructor) - } - } - - clazz?.properties?.forEach { property -> - val propertyContext = context.create(property.typeParameters) - processProperty(propertyContext, classNode, property) - } - clazz?.functions?.forEach { function -> - val functionContext = context.create(function.typeParameters) - processFunction(functionContext, classNode, function) - } - - classesToProcess.forEachClassInScope(parentClassId = classId) { nestedClassEntry -> - val nestedClassContext = context.create(nestedClassEntry) - processClass(nestedClassContext, classNode, nestedClassEntry, classesToProcess) - } - } - - private fun processClassConstructor( - context: CirTreeMergingContext, - classNode: CirClassNode, - constructor: KmConstructor - ) { - val approximationKey = ConstructorApproximationKey(constructor, context.typeResolver) - val constructorNode: CirClassConstructorNode = classNode.constructors.getOrPut(approximationKey) { - buildClassConstructorNode(storageManager, leafTargetsSize, classifiers, classNode.commonDeclaration) - } - constructorNode.targetDeclarations[context.targetIndex] = CirDeserializers.constructor( - source = constructor, - containingClass = classNode.targetDeclarations[context.targetIndex]!!, - typeResolver = context.typeResolver - ) - } - - private fun processTypeAlias( - context: CirTreeMergingContext, - packageNode: CirPackageNode, - typeAlias: KmTypeAlias - ) { - val typeAliasName = CirName.create(typeAlias.name) - val typeAliasId = CirEntityId.create(packageNode.packageName, typeAliasName) - - val typeAliasNode: CirTypeAliasNode = packageNode.typeAliases.getOrPut(typeAliasName) { - buildTypeAliasNode(storageManager, leafTargetsSize, classifiers, typeAliasId) - } - typeAliasNode.targetDeclarations[context.targetIndex] = CirDeserializers.typeAlias( - name = typeAliasName, - source = typeAlias, - typeResolver = context.typeResolver - ) - } -} - -private class ClassesToProcess { - sealed class ClassEntry { - abstract val classId: CirEntityId - - data class RegularClassEntry( - override val classId: CirEntityId, - val clazz: KmClass - ) : ClassEntry() - - data class EnumEntry( - override val classId: CirEntityId, - val annotations: List, - val enumClassId: CirEntityId, - val enumClass: KmClass - ) : ClassEntry() - } - - // key = parent class ID (or NON_EXISTING_CLASSIFIER_ID for top-level classes) - // value = classes under this parent class (MutableList to preserve order of classes) - private val groupedByParentClassId = FactoryMap.create> { ArrayList() } - - fun addClassesFromFragment(fragment: KmModuleFragment) { - val klibEnumEntries = LinkedHashMap() // linked hash map to preserve order - val regularClassIds = HashSet() - - fragment.classes.forEach { clazz -> - val classId: CirEntityId = CirEntityId.create(clazz.name) - val parentClassId: CirEntityId = classId.getParentEntityId() ?: NON_EXISTING_CLASSIFIER_ID - - if (Flag.Class.IS_ENUM_CLASS(clazz.flags)) { - clazz.klibEnumEntries.forEach { entry -> - val enumEntryId = classId.createNestedEntityId(CirName.create(entry.name)) - klibEnumEntries[enumEntryId] = ClassEntry.EnumEntry(enumEntryId, entry.annotations, classId, clazz) - } - } - - groupedByParentClassId.getValue(parentClassId) += ClassEntry.RegularClassEntry(classId, clazz) - regularClassIds += classId - } - - // add enum entries that are not stored in module as KmClass records - klibEnumEntries.forEach { (enumEntryId, enumEntry) -> - if (enumEntryId !in regularClassIds) { - groupedByParentClassId.getValue(enumEntry.enumClassId) += enumEntry - } - } - } - - fun forEachClassInScope(parentClassId: CirEntityId?, block: (ClassEntry) -> Unit) { - groupedByParentClassId[parentClassId ?: NON_EXISTING_CLASSIFIER_ID]?.forEach { classEntry -> block(classEntry) } - } + return rootMerger.processRoot(storageManager, classifiers, parameters) } diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassConstructorMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassConstructorMerger.kt new file mode 100644 index 00000000000..13c82728a4e --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassConstructorMerger.kt @@ -0,0 +1,30 @@ +/* + * 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.mergedtree.mergers + +import kotlinx.metadata.KmConstructor +import org.jetbrains.kotlin.commonizer.mergedtree.* +import org.jetbrains.kotlin.commonizer.mergedtree.CirTargetMergingContext +import org.jetbrains.kotlin.commonizer.mergedtree.buildClassConstructorNode +import org.jetbrains.kotlin.commonizer.metadata.CirDeserializers + +internal object ClassConstructorMerger { + fun processClassConstructor( + context: CirTargetMergingContext, + classNode: CirClassNode, + constructor: KmConstructor + ) = with(context) { + val approximationKey = ConstructorApproximationKey(constructor, context.typeResolver) + val constructorNode: CirClassConstructorNode = classNode.constructors.getOrPut(approximationKey) { + buildClassConstructorNode(storageManager, targets, classifiers, classNode.commonDeclaration) + } + constructorNode.targetDeclarations[context.targetIndex] = CirDeserializers.constructor( + source = constructor, + containingClass = classNode.targetDeclarations[context.targetIndex]!!, + typeResolver = context.typeResolver + ) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassMerger.kt new file mode 100644 index 00000000000..5261cb487e0 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassMerger.kt @@ -0,0 +1,80 @@ +/* + * 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.mergedtree.mergers + +import kotlinx.metadata.Flag +import kotlinx.metadata.KmClass +import org.jetbrains.kotlin.commonizer.mergedtree.* +import org.jetbrains.kotlin.commonizer.mergedtree.CirTargetMergingContext +import org.jetbrains.kotlin.commonizer.mergedtree.buildClassNode +import org.jetbrains.kotlin.commonizer.metadata.CirDeserializers + +internal class ClassMerger( + private val classConstructorMerger: ClassConstructorMerger?, + private val propertyMerger: PropertyMerger?, + private val functionMerger: FunctionMerger? +) { + fun processClass( + context: CirTargetMergingContext, + ownerNode: CirNodeWithMembers<*, *>, + classEntry: ClassesToProcess.ClassEntry, + classesToProcess: ClassesToProcess + ): Unit = with(context) { + val classId = classEntry.classId + val className = classId.relativeNameSegments.last() + + val maybeClassOwnerNode: CirClassNode? = ownerNode as? CirClassNode + val classNode: CirClassNode = ownerNode.classes.getOrPut(className) { + buildClassNode(storageManager, targets, classifiers, maybeClassOwnerNode?.commonDeclaration, classId) + } + + val clazz: KmClass? + val isEnumEntry: Boolean + + classNode.targetDeclarations[context.targetIndex] = when (classEntry) { + is ClassesToProcess.ClassEntry.RegularClassEntry -> { + clazz = classEntry.clazz + isEnumEntry = Flag.Class.IS_ENUM_ENTRY(clazz.flags) + + CirDeserializers.clazz(className, clazz, context.typeResolver) + } + is ClassesToProcess.ClassEntry.EnumEntry -> { + clazz = null + isEnumEntry = true + + CirDeserializers.defaultEnumEntry( + name = className, + annotations = classEntry.annotations, + enumClassId = classEntry.enumClassId, + enumClass = classEntry.enumClass, + typeResolver = context.typeResolver + ) + } + } + + if (!isEnumEntry) { + clazz?.constructors?.forEach { constructor -> + // TODO: nowhere to read constructor type parameters from + //val constructorContext = context.create(constructor.typeParameters) + classConstructorMerger?.processClassConstructor(context, classNode, constructor) + } + } + + clazz?.properties?.forEach { property -> + val propertyContext = context.create(property.typeParameters) + propertyMerger?.processProperty(propertyContext, classNode, property) + } + clazz?.functions?.forEach { function -> + val functionContext = context.create(function.typeParameters) + functionMerger?.processFunction(functionContext, classNode, function) + } + + classesToProcess.forEachClassInScope(parentClassId = classId) { nestedClassEntry -> + val nestedClassContext = context.create(nestedClassEntry) + processClass(nestedClassContext, classNode, nestedClassEntry, classesToProcess) + } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassesToProcess.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassesToProcess.kt new file mode 100644 index 00000000000..891e8a4411e --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ClassesToProcess.kt @@ -0,0 +1,69 @@ +/* + * 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.mergedtree.mergers + +import com.intellij.util.containers.FactoryMap +import kotlinx.metadata.Flag +import kotlinx.metadata.KmAnnotation +import kotlinx.metadata.KmClass +import kotlinx.metadata.KmModuleFragment +import kotlinx.metadata.klib.klibEnumEntries +import org.jetbrains.kotlin.commonizer.cir.CirEntityId +import org.jetbrains.kotlin.commonizer.cir.CirName +import org.jetbrains.kotlin.commonizer.utils.NON_EXISTING_CLASSIFIER_ID + +internal class ClassesToProcess { + sealed class ClassEntry { + abstract val classId: CirEntityId + + data class RegularClassEntry( + override val classId: CirEntityId, + val clazz: KmClass + ) : ClassEntry() + + data class EnumEntry( + override val classId: CirEntityId, + val annotations: List, + val enumClassId: CirEntityId, + val enumClass: KmClass + ) : ClassEntry() + } + + // key = parent class ID (or NON_EXISTING_CLASSIFIER_ID for top-level classes) + // value = classes under this parent class (MutableList to preserve order of classes) + private val groupedByParentClassId = FactoryMap.create> { ArrayList() } + + fun addClassesFromFragment(fragment: KmModuleFragment) { + val klibEnumEntries = LinkedHashMap() // linked hash map to preserve order + val regularClassIds = HashSet() + + fragment.classes.forEach { clazz -> + val classId: CirEntityId = CirEntityId.create(clazz.name) + val parentClassId: CirEntityId = classId.getParentEntityId() ?: NON_EXISTING_CLASSIFIER_ID + + if (Flag.Class.IS_ENUM_CLASS(clazz.flags)) { + clazz.klibEnumEntries.forEach { entry -> + val enumEntryId = classId.createNestedEntityId(CirName.create(entry.name)) + klibEnumEntries[enumEntryId] = ClassEntry.EnumEntry(enumEntryId, entry.annotations, classId, clazz) + } + } + + groupedByParentClassId.getValue(parentClassId) += ClassEntry.RegularClassEntry(classId, clazz) + regularClassIds += classId + } + + // add enum entries that are not stored in module as KmClass records + klibEnumEntries.forEach { (enumEntryId, enumEntry) -> + if (enumEntryId !in regularClassIds) { + groupedByParentClassId.getValue(enumEntry.enumClassId) += enumEntry + } + } + } + + fun forEachClassInScope(parentClassId: CirEntityId?, block: (ClassEntry) -> Unit) { + groupedByParentClassId[parentClassId ?: NON_EXISTING_CLASSIFIER_ID]?.forEach { classEntry -> block(classEntry) } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/FunctionMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/FunctionMerger.kt new file mode 100644 index 00000000000..730c8825009 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/FunctionMerger.kt @@ -0,0 +1,43 @@ +/* + * 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.mergedtree.mergers + +import kotlinx.metadata.KmFunction +import org.jetbrains.kotlin.commonizer.mergedtree.* +import org.jetbrains.kotlin.commonizer.mergedtree.CirTargetMergingContext +import org.jetbrains.kotlin.commonizer.mergedtree.buildFunctionNode +import org.jetbrains.kotlin.commonizer.metadata.CirDeserializers +import org.jetbrains.kotlin.commonizer.utils.isFakeOverride +import org.jetbrains.kotlin.commonizer.utils.isKniBridgeFunction +import org.jetbrains.kotlin.commonizer.utils.isTopLevelDeprecatedFunction + +internal object FunctionMerger { + fun processFunction( + context: CirTargetMergingContext, + ownerNode: CirNodeWithMembers<*, *>, + function: KmFunction + ) = with(context) { + if (function.isFakeOverride() + || function.isKniBridgeFunction() + || function.isTopLevelDeprecatedFunction(isTopLevel = ownerNode !is CirClassNode) + ) { + return + } + + val maybeClassOwnerNode: CirClassNode? = ownerNode as? CirClassNode + + val approximationKey = FunctionApproximationKey(function, context.typeResolver) + val functionNode: CirFunctionNode = ownerNode.functions.getOrPut(approximationKey) { + buildFunctionNode(storageManager, targets, classifiers, maybeClassOwnerNode?.commonDeclaration) + } + functionNode.targetDeclarations[context.targetIndex] = CirDeserializers.function( + name = approximationKey.name, + source = function, + containingClass = maybeClassOwnerNode?.targetDeclarations?.get(context.targetIndex), + typeResolver = context.typeResolver + ) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ModuleMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ModuleMerger.kt new file mode 100644 index 00000000000..b1a5576c483 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/ModuleMerger.kt @@ -0,0 +1,42 @@ +/* + * 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.mergedtree.mergers + +import kotlinx.metadata.KmModuleFragment +import kotlinx.metadata.klib.KlibModuleMetadata +import kotlinx.metadata.klib.fqName +import org.jetbrains.kotlin.commonizer.cir.CirModule +import org.jetbrains.kotlin.commonizer.cir.CirName +import org.jetbrains.kotlin.commonizer.cir.CirPackageName +import org.jetbrains.kotlin.commonizer.mergedtree.CirModuleNode +import org.jetbrains.kotlin.commonizer.mergedtree.CirRootNode +import org.jetbrains.kotlin.commonizer.mergedtree.CirTargetMergingContext +import org.jetbrains.kotlin.commonizer.mergedtree.buildModuleNode +import org.jetbrains.kotlin.commonizer.utils.foldToMap + +internal class ModuleMerger( + private val packageMerger: PackageMerger +) { + fun processModule( + context: CirTargetMergingContext, + rootNode: CirRootNode, + module: KlibModuleMetadata + ): Unit = with(context) { + val moduleName: CirName = CirName.create(module.name) + val moduleNode: CirModuleNode = rootNode.modules.getOrPut(moduleName) { + buildModuleNode(storageManager, targets) + } + moduleNode.targetDeclarations[context.targetIndex] = CirModule.create(moduleName) + + val groupedFragments: Map> = module.fragments.foldToMap { fragment -> + fragment.fqName?.let(CirPackageName.Companion::create) ?: error("A fragment without FQ name in module $moduleName: $fragment") + } + + groupedFragments.forEach { (packageName, fragments) -> + packageMerger.processFragments(context, moduleNode, fragments, packageName) + } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/PackageMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/PackageMerger.kt new file mode 100644 index 00000000000..ad2db359b7b --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/PackageMerger.kt @@ -0,0 +1,58 @@ +/* + * 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.mergedtree.mergers + +import kotlinx.metadata.KmModuleFragment +import kotlinx.metadata.KmTypeParameter +import org.jetbrains.kotlin.commonizer.cir.CirPackage +import org.jetbrains.kotlin.commonizer.cir.CirPackageName +import org.jetbrains.kotlin.commonizer.mergedtree.* +import org.jetbrains.kotlin.commonizer.mergedtree.CirTargetMergingContext +import org.jetbrains.kotlin.commonizer.mergedtree.buildPackageNode + +internal class PackageMerger( + private val propertyMerger: PropertyMerger?, + private val functionMerger: FunctionMerger?, + private val classMerger: ClassMerger?, + private val typeAliasMerger: TypeAliasMerger? +) { + fun processFragments( + context: CirTargetMergingContext, + moduleNode: CirModuleNode, + fragments: Collection, + packageName: CirPackageName + ): Unit = with(context) { + val packageNode: CirPackageNode = moduleNode.packages.getOrPut(packageName) { + buildPackageNode(storageManager, targets) + } + packageNode.targetDeclarations[context.targetIndex] = CirPackage.create(packageName) + + val classesToProcess = ClassesToProcess() + fragments.forEach { fragment -> + classesToProcess.addClassesFromFragment(fragment) + + fragment.pkg?.let { pkg -> + pkg.properties.forEach { property -> + val propertyContext = context.create(property.typeParameters) + propertyMerger?.processProperty(propertyContext, packageNode, property) + } + pkg.functions.forEach { function -> + val functionContext = context.create(function.typeParameters) + functionMerger?.processFunction(functionContext, packageNode, function) + } + pkg.typeAliases.forEach { typeAlias -> + val typeAliasContext = context.create(typeAlias.typeParameters) + typeAliasMerger?.processTypeAlias(typeAliasContext, packageNode, typeAlias) + } + } + } + + classesToProcess.forEachClassInScope(parentClassId = null) { classEntry -> + val classContext = context.create(classEntry) + classMerger?.processClass(classContext, packageNode, classEntry, classesToProcess) + } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/PropertyMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/PropertyMerger.kt new file mode 100644 index 00000000000..f54f65c3033 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/PropertyMerger.kt @@ -0,0 +1,37 @@ +/* + * 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.mergedtree.mergers + +import kotlinx.metadata.KmProperty +import org.jetbrains.kotlin.commonizer.mergedtree.* +import org.jetbrains.kotlin.commonizer.mergedtree.CirTargetMergingContext +import org.jetbrains.kotlin.commonizer.mergedtree.buildPropertyNode +import org.jetbrains.kotlin.commonizer.metadata.CirDeserializers +import org.jetbrains.kotlin.commonizer.utils.isFakeOverride + +internal object PropertyMerger { + fun processProperty( + context: CirTargetMergingContext, + ownerNode: CirNodeWithMembers<*, *>, + property: KmProperty + ) = with(context) { + if (property.isFakeOverride()) + return + + val maybeClassOwnerNode: CirClassNode? = ownerNode as? CirClassNode + + val approximationKey = PropertyApproximationKey(property, context.typeResolver) + val propertyNode: CirPropertyNode = ownerNode.properties.getOrPut(approximationKey) { + buildPropertyNode(storageManager, targets, classifiers, maybeClassOwnerNode?.commonDeclaration) + } + propertyNode.targetDeclarations[context.targetIndex] = CirDeserializers.property( + name = approximationKey.name, + source = property, + containingClass = maybeClassOwnerNode?.targetDeclarations?.get(context.targetIndex), + typeResolver = context.typeResolver + ) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/RootMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/RootMerger.kt new file mode 100644 index 00000000000..bcc4a3673bc --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/RootMerger.kt @@ -0,0 +1,58 @@ +/* + * 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.mergedtree.mergers + +import org.jetbrains.kotlin.commonizer.CommonizerParameters +import org.jetbrains.kotlin.commonizer.LeafCommonizerTarget +import org.jetbrains.kotlin.commonizer.ModulesProvider +import org.jetbrains.kotlin.commonizer.mergedtree.* +import org.jetbrains.kotlin.commonizer.mergedtree.buildRootNode +import org.jetbrains.kotlin.commonizer.metadata.CirTypeResolver +import org.jetbrains.kotlin.commonizer.prettyName +import org.jetbrains.kotlin.storage.StorageManager + +internal class RootMerger( + private val targetMerger: TargetMerger +) { + fun processRoot( + storageManager: StorageManager, + classifiers: CirKnownClassifiers, + parameters: CommonizerParameters + ): CirTreeMergeResult { + val rootNode: CirRootNode = buildRootNode(storageManager, parameters.targetProviders.size) + + val commonModuleNames = parameters.getCommonModuleNames() + val missingModuleInfosByTargets = mutableMapOf>() + + parameters.targetProviders.forEachIndexed { targetIndex, targetProvider -> + val allModuleInfos = targetProvider.modulesProvider.loadModuleInfos() + + val (commonModuleInfos, missingModuleInfos) = allModuleInfos.partition { it.name in commonModuleNames } + val typeResolver = CirTypeResolver.create( + providedClassifiers = CirProvidedClassifiers.of( + classifiers.commonDependencies, + CirProvidedClassifiers.by(targetProvider.dependencyModulesProvider), + CirProvidedClassifiers.by(targetProvider.modulesProvider) + ) + ) + targetMerger.processTarget( + CirTargetMergingContext( + storageManager, classifiers, parameters, targetIndex, typeResolver + ), rootNode, targetProvider, commonModuleInfos + ) + + missingModuleInfosByTargets[targetProvider.target] = missingModuleInfos + + parameters.progressLogger?.invoke("Loaded declarations for ${targetProvider.target.prettyName}") + System.gc() + } + + return CirTreeMergeResult( + root = rootNode, + missingModuleInfos = missingModuleInfosByTargets + ) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/TargetMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/TargetMerger.kt new file mode 100644 index 00000000000..812da0564ec --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/TargetMerger.kt @@ -0,0 +1,37 @@ +/* + * 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.mergedtree.mergers + +import kotlinx.metadata.klib.KlibModuleMetadata +import org.jetbrains.kotlin.commonizer.ModulesProvider +import org.jetbrains.kotlin.commonizer.TargetProvider +import org.jetbrains.kotlin.commonizer.cir.CirRoot +import org.jetbrains.kotlin.commonizer.mergedtree.CirRootNode +import org.jetbrains.kotlin.commonizer.mergedtree.CirTargetMergingContext +import org.jetbrains.kotlin.commonizer.metadata.utils.SerializedMetadataLibraryProvider + +internal class TargetMerger( + private val moduleMerger: ModuleMerger +) { + fun processTarget( + context: CirTargetMergingContext, + rootNode: CirRootNode, + targetProvider: TargetProvider, + commonModuleInfos: Collection + ): Unit = with(context) { + rootNode.targetDeclarations[targetIndex] = CirRoot.create(targetProvider.target) + + if (commonModuleInfos.isEmpty()) { + return + } + + commonModuleInfos.forEach { moduleInfo -> + val metadata = targetProvider.modulesProvider.loadModuleMetadata(moduleInfo.name) + val module = KlibModuleMetadata.read(SerializedMetadataLibraryProvider(metadata)) + moduleMerger.processModule(context, rootNode, module) + } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/TypeAliasMerger.kt b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/TypeAliasMerger.kt new file mode 100644 index 00000000000..48375681b0e --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/commonizer/mergedtree/mergers/TypeAliasMerger.kt @@ -0,0 +1,35 @@ +/* + * 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.mergedtree.mergers + +import kotlinx.metadata.KmTypeAlias +import org.jetbrains.kotlin.commonizer.cir.CirEntityId +import org.jetbrains.kotlin.commonizer.cir.CirName +import org.jetbrains.kotlin.commonizer.mergedtree.CirPackageNode +import org.jetbrains.kotlin.commonizer.mergedtree.CirTargetMergingContext +import org.jetbrains.kotlin.commonizer.mergedtree.CirTypeAliasNode +import org.jetbrains.kotlin.commonizer.mergedtree.buildTypeAliasNode +import org.jetbrains.kotlin.commonizer.metadata.CirDeserializers + +internal object TypeAliasMerger { + fun processTypeAlias( + context: CirTargetMergingContext, + packageNode: CirPackageNode, + typeAlias: KmTypeAlias + ) = with(context) { + val typeAliasName = CirName.create(typeAlias.name) + val typeAliasId = CirEntityId.create(packageNode.packageName, typeAliasName) + + val typeAliasNode: CirTypeAliasNode = packageNode.typeAliases.getOrPut(typeAliasName) { + buildTypeAliasNode(storageManager, targets, classifiers, typeAliasId) + } + typeAliasNode.targetDeclarations[context.targetIndex] = CirDeserializers.typeAlias( + name = typeAliasName, + source = typeAlias, + typeResolver = context.typeResolver + ) + } +}