From 2bcaf1fa635eff9035cf764a67783655cc63f38b Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Wed, 8 Jul 2020 14:39:41 +0700 Subject: [PATCH] [Commonizer] Introduce metadata builder --- .../commonizer/cir/factory/CirTypeFactory.kt | 10 + .../commonizer/core/CommonizationVisitor.kt | 15 +- .../commonizer/core/TypeAliasCommonizer.kt | 9 +- .../commonizer/core/typeAliasUtils.kt | 7 + .../mergedtree/CirNodeWithLiftingUp.kt | 14 + .../commonizer/mergedtree/CirPropertyNode.kt | 2 +- .../commonizer/mergedtree/CirTypeAliasNode.kt | 2 +- .../commonizer/metadata/builders.kt | 357 ++++++++++++++++++ .../commonizer/metadata/context.kt | 60 +++ .../descriptors/commonizer/metadata/flags.kt | 179 +++++++++ .../commonizer/metadata/visitor.kt | 134 +++++++ .../descriptors/commonizer/utils/fqName.kt | 8 + 12 files changed, 782 insertions(+), 15 deletions(-) create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirNodeWithLiftingUp.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/builders.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/context.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/flags.kt create mode 100644 native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/visitor.kt diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeFactory.kt index 03b09cef95d..488d36d26e9 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeFactory.kt @@ -18,6 +18,16 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.types.* object CirTypeFactory { + object StandardTypes { + val ANY: CirClassType = createClassType( + classId = ANY_CID, + outerType = null, + visibility = DescriptorVisibilities.PUBLIC, + arguments = emptyList(), + isMarkedNullable = false + ) + } + private val classTypeInterner = Interner() private val typeAliasTypeInterner = Interner() private val typeParameterTypeInterner = Interner() diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/CommonizationVisitor.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/CommonizationVisitor.kt index 366833e705b..103885a4d94 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/CommonizationVisitor.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/CommonizationVisitor.kt @@ -7,10 +7,12 @@ package org.jetbrains.kotlin.descriptors.commonizer.core import org.jetbrains.kotlin.descriptors.commonizer.cir.CirClass import org.jetbrains.kotlin.descriptors.commonizer.cir.CirType +import org.jetbrains.kotlin.descriptors.commonizer.cir.factory.CirTypeFactory import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.* -import org.jetbrains.kotlin.descriptors.commonizer.utils.CommonizedGroup +import org.jetbrains.kotlin.descriptors.commonizer.utils.* import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMapNotNull import org.jetbrains.kotlin.descriptors.commonizer.utils.internedClassId +import org.jetbrains.kotlin.name.ClassId internal class CommonizationVisitor( private val classifiers: CirKnownClassifiers, @@ -97,7 +99,7 @@ internal class CommonizationVisitor( } // find out common (and commonized) supertypes - commonClass.commonizeSupertypes(node.collectCommonSupertypes()) + commonClass.commonizeSupertypes(node.classId, node.collectCommonSupertypes()) } } @@ -110,7 +112,7 @@ internal class CommonizationVisitor( if (commonClassifier is CirClass) { // find out common (and commonized) supertypes - commonClassifier.commonizeSupertypes(node.collectCommonSupertypes()) + commonClassifier.commonizeSupertypes(node.classId, node.collectCommonSupertypes()) } } @@ -142,10 +144,13 @@ internal class CommonizationVisitor( return supertypesMap } - private fun CirClass.commonizeSupertypes(supertypesMap: Map>?) { + private fun CirClass.commonizeSupertypes( + classId: ClassId, + supertypesMap: Map>? + ) { setSupertypes( if (supertypesMap.isNullOrEmpty()) - emptyList() + if (classId in SPECIAL_CLASS_WITHOUT_SUPERTYPES_CIDS) emptyList() else listOf(CirTypeFactory.StandardTypes.ANY) else supertypesMap.values.compactMapNotNull { supertypesGroup -> commonize(supertypesGroup, TypeCommonizer(classifiers)) } ) diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeAliasCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeAliasCommonizer.kt index 56f36d60905..1c3d6629fd5 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeAliasCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeAliasCommonizer.kt @@ -93,7 +93,7 @@ private class TypeAliasLiftingUpCommonizer(classifiers: CirKnownClassifiers) : A typeParameters = typeParameters.result, visibility = visibility.result, underlyingType = underlyingType, - expandedType = computeCommonizedExpandedType(underlyingType) + expandedType = computeExpandedType(underlyingType) ) } @@ -108,13 +108,6 @@ private class TypeAliasLiftingUpCommonizer(classifiers: CirKnownClassifiers) : A typeParameters.commonizeWith(next.typeParameters) && underlyingType.commonizeWith(next.underlyingType) && visibility.commonizeWith(next) - - private tailrec fun computeCommonizedExpandedType(underlyingType: CirClassOrTypeAliasType): CirClassType { - return when (underlyingType) { - is CirClassType -> underlyingType - is CirTypeAliasType -> computeCommonizedExpandedType(underlyingType.underlyingType) - } - } } private class TypeAliasExpectClassCommonizer : AbstractStandardCommonizer() { diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/typeAliasUtils.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/typeAliasUtils.kt index c5f1697ff1b..7d3dcd81241 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/typeAliasUtils.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/typeAliasUtils.kt @@ -12,6 +12,13 @@ import org.jetbrains.kotlin.descriptors.commonizer.cir.CirTypeProjection import org.jetbrains.kotlin.descriptors.commonizer.cir.factory.CirTypeFactory import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirKnownClassifiers +tailrec fun computeExpandedType(underlyingType: CirClassOrTypeAliasType): CirClassType { + return when (underlyingType) { + is CirClassType -> underlyingType + is CirTypeAliasType -> computeExpandedType(underlyingType.underlyingType) + } +} + internal tailrec fun computeSuitableUnderlyingType( classifiers: CirKnownClassifiers, underlyingType: CirClassOrTypeAliasType diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirNodeWithLiftingUp.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirNodeWithLiftingUp.kt new file mode 100644 index 00000000000..864728f4497 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirNodeWithLiftingUp.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2020 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.descriptors.commonizer.mergedtree + +import org.jetbrains.kotlin.descriptors.commonizer.cir.CirDeclaration +import org.jetbrains.kotlin.descriptors.commonizer.cir.CirLiftedUpDeclaration + +interface CirNodeWithLiftingUp : CirNode { + val isLiftedUp: Boolean + get() = (commonDeclaration() as? CirLiftedUpDeclaration)?.isLiftedUp == true +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirPropertyNode.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirPropertyNode.kt index 31d903c5291..2557608e587 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirPropertyNode.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirPropertyNode.kt @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.storage.NullableLazyValue class CirPropertyNode( override val targetDeclarations: CommonizedGroup, override val commonDeclaration: NullableLazyValue -) : CirNode { +) : CirNodeWithLiftingUp { override fun accept(visitor: CirNodeVisitor, data: T) = visitor.visitPropertyNode(this, data) diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirTypeAliasNode.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirTypeAliasNode.kt index 723eebbb162..06f0a464fbd 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirTypeAliasNode.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirTypeAliasNode.kt @@ -15,7 +15,7 @@ class CirTypeAliasNode( override val targetDeclarations: CommonizedGroup, override val commonDeclaration: NullableLazyValue, override val classId: ClassId -) : CirNodeWithClassId { +) : CirNodeWithClassId, CirNodeWithLiftingUp { override fun accept(visitor: CirNodeVisitor, data: T): R = visitor.visitTypeAliasNode(this, data) diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/builders.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/builders.kt new file mode 100644 index 00000000000..de3a9ebf672 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/builders.kt @@ -0,0 +1,357 @@ +/* + * Copyright 2010-2020 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.descriptors.commonizer.metadata + +import kotlinx.metadata.* +import kotlinx.metadata.klib.* +import org.jetbrains.kotlin.backend.common.serialization.metadata.DynamicTypeDeserializer +import org.jetbrains.kotlin.descriptors.commonizer.cir.* +import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirValueParameterImpl +import org.jetbrains.kotlin.descriptors.commonizer.core.computeExpandedType +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.* +import org.jetbrains.kotlin.descriptors.commonizer.metadata.TypeAliasExpansion.* +import org.jetbrains.kotlin.descriptors.commonizer.utils.strip +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.constants.* +import org.jetbrains.kotlin.types.Variance + +internal fun CirModule.buildModule( + fragments: Collection +): KlibModuleMetadata = KlibModuleMetadata( + name = name.strip(), + fragments = fragments.toList(), + annotations = emptyList() +) + +internal fun CirPackage.buildModuleFragment( + allClasses: Collection, + topLevelTypeAliases: Collection, + topLevelFunctions: Collection, + topLevelProperties: Collection +): KmModuleFragment = KmModuleFragment().also { fragment -> + fragment.fqName = fqName.asString() + allClasses.forEach { + fragment.classes += it + fragment.className += it.name + } + + if (topLevelTypeAliases.isNotEmpty() || topLevelFunctions.isNotEmpty() || topLevelProperties.isNotEmpty()) { + fragment.pkg = KmPackage().also { pkg -> + pkg.fqName = fqName.asString() + pkg.typeAliases += topLevelTypeAliases + pkg.functions += topLevelFunctions + pkg.properties += topLevelProperties + } + } +} + +internal fun addEmptyFragments(fragments: MutableCollection) { + val existingPackageFqNames: Set = fragments.mapTo(HashSet()) { it.fqName!! } + + val missingPackageFqNames: Set = existingPackageFqNames.flatMapTo(HashSet()) { fqName -> + fqName.mapIndexedNotNull { index, ch -> + if (ch == '.') { + val parentFqName = fqName.substring(0, index) + if (parentFqName !in existingPackageFqNames) + return@mapIndexedNotNull parentFqName + } + + null + } + } + + missingPackageFqNames.forEach { fqName -> + fragments += KmModuleFragment().also { fragment -> + fragment.fqName = fqName + } + } +} + +internal fun CirClass.buildClass( + context: VisitingContext, + className: ClassName, + directNestedClasses: Collection, + nestedConstructors: Collection, + nestedFunctions: Collection, + nestedProperties: Collection +): KmClass = KmClass().also { clazz -> + clazz.flags = classFlags(isExpect = context.isCommon) + annotations.mapTo(clazz.annotations) { it.buildAnnotation() } + typeParameters.buildTypeParameters(context, output = clazz.typeParameters) + clazz.name = className + + clazz.constructors += nestedConstructors + clazz.functions += nestedFunctions + clazz.properties += nestedProperties + + directNestedClasses.forEach { directNestedClass -> + val shortClassName = directNestedClass.name.substringAfterLast('.') + + if (Flag.Class.IS_ENUM_ENTRY(directNestedClass.flags)) { + clazz.enumEntries += shortClassName + clazz.klibEnumEntries += KlibEnumEntry(name = shortClassName, annotations = directNestedClass.annotations) + } else { + clazz.nestedClasses += shortClassName + } + } + + clazz.companionObject = companion?.asString() + supertypes.mapTo(clazz.supertypes) { it.buildType(context) } +} + +internal fun linkSealedClassesWithSubclasses(packageFqName: FqName, classConsumer: ClassConsumer) { + if (classConsumer.allClasses.isEmpty() || classConsumer.sealedClasses.isEmpty()) return + + val packageName = packageFqName.asString().replace('.', '/') + fun ClassName.isInSamePackage(): Boolean = substringBeforeLast('/', "") == packageName + + val sealedClassesMap: Map = classConsumer.sealedClasses.associateBy { it.name } + + classConsumer.allClasses.forEach { clazz -> + clazz.supertypes.forEach supertype@{ supertype -> + val superclassName = (supertype.classifier as? KmClassifier.Class)?.name ?: return@supertype + if (!superclassName.isInSamePackage()) return@supertype + val sealedClass = sealedClassesMap[superclassName] ?: return@supertype + sealedClass.sealedSubclasses += clazz.name + } + } +} + +internal fun CirClassConstructor.buildClassConstructor( + context: VisitingContext +): KmConstructor = KmConstructor( + flags = classConstructorFlags() +).also { constructor -> + annotations.mapTo(constructor.annotations) { it.buildAnnotation() } + valueParameters.mapTo(constructor.valueParameters) { it.buildValueParameter(context) } +} + +internal fun CirTypeAlias.buildTypeAlias( + context: VisitingContext +): KmTypeAlias = KmTypeAlias( + flags = typeAliasFlags(), + name = name.asString() +).also { typeAlias -> + annotations.mapTo(typeAlias.annotations) { it.buildAnnotation() } + typeParameters.buildTypeParameters(context, output = typeAlias.typeParameters) + typeAlias.underlyingType = underlyingType.buildType(context, expansion = ONLY_ABBREVIATIONS) + typeAlias.expandedType = underlyingType.buildType(context, expansion = FOR_TOP_LEVEL_TYPE) +} + +internal fun CirProperty.buildProperty( + context: VisitingContext, +): KmProperty = KmProperty( + flags = propertyFlags(isExpect = context.isCommon && !isLiftedUp), + name = name.asString(), + getterFlags = getter?.propertyAccessorFlags(this, this) ?: NO_FLAGS, + setterFlags = setter?.let { setter -> setter.propertyAccessorFlags(setter, this) } ?: NO_FLAGS +).also { property -> + annotations.mapTo(property.annotations) { it.buildAnnotation() } + getter?.annotations?.mapTo(property.getterAnnotations) { it.buildAnnotation() } + setter?.annotations?.mapTo(property.setterAnnotations) { it.buildAnnotation() } + property.compileTimeValue = compileTimeInitializer?.takeIf { it !is NullValue }?.buildAnnotationArgument() + typeParameters.buildTypeParameters(context, output = property.typeParameters) + extensionReceiver?.let { receiver -> + // TODO nowhere to write receiver annotations, see KT-42490 + property.receiverParameterType = receiver.type.buildType(context) + } + setter?.takeIf { !it.isDefault }?.let { setter -> + property.setterParameter = CirValueParameterImpl( + annotations = setter.parameterAnnotations, + name = SETTER_VALUE_NAME, + returnType = returnType, + varargElementType = null, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false + ).buildValueParameter(context) + } + property.returnType = returnType.buildType(context) +} + +internal fun CirFunction.buildFunction( + context: VisitingContext, +): KmFunction = KmFunction( + flags = functionFlags(isExpect = context.isCommon), + name = name.asString() +).also { function -> + annotations.mapTo(function.annotations) { it.buildAnnotation() } + typeParameters.buildTypeParameters(context, output = function.typeParameters) + valueParameters.mapTo(function.valueParameters) { it.buildValueParameter(context) } + extensionReceiver?.let { receiver -> + // TODO nowhere to write receiver annotations, see KT-42490 + function.receiverParameterType = receiver.type.buildType(context) + } + function.returnType = returnType.buildType(context) +} + +private fun CirAnnotation.buildAnnotation(): KmAnnotation { + val arguments = LinkedHashMap>(constantValueArguments.size + annotationValueArguments.size, 1F) + + constantValueArguments.forEach { (name: Name, value: ConstantValue<*>) -> + arguments[name.asString()] = value.buildAnnotationArgument() + } + + annotationValueArguments.forEach { (name: Name, nested: CirAnnotation) -> + arguments[name.asString()] = KmAnnotationArgument.AnnotationValue(nested.buildAnnotation()) + } + + return KmAnnotation( + className = type.classifierId.asString(), + arguments = arguments + ) +} + +private fun ConstantValue<*>.buildAnnotationArgument(): KmAnnotationArgument<*> = when (this) { + is StringValue -> KmAnnotationArgument.StringValue(value) + is CharValue -> KmAnnotationArgument.CharValue(value) + + is ByteValue -> KmAnnotationArgument.ByteValue(value) + is ShortValue -> KmAnnotationArgument.ShortValue(value) + is IntValue -> KmAnnotationArgument.IntValue(value) + is LongValue -> KmAnnotationArgument.LongValue(value) + + is UByteValue -> KmAnnotationArgument.UByteValue(value) + is UShortValue -> KmAnnotationArgument.UShortValue(value) + is UIntValue -> KmAnnotationArgument.UIntValue(value) + is ULongValue -> KmAnnotationArgument.ULongValue(value) + + is FloatValue -> KmAnnotationArgument.FloatValue(value) + is DoubleValue -> KmAnnotationArgument.DoubleValue(value) + is BooleanValue -> KmAnnotationArgument.BooleanValue(value) + + is EnumValue -> KmAnnotationArgument.EnumValue(enumClassId.asString(), enumEntryName.asString()) + is ArrayValue -> KmAnnotationArgument.ArrayValue(value.map { it.buildAnnotationArgument() }) + + else -> error("Unsupported annotation argument type: ${this::class.java}, $this") +} + +private fun CirValueParameter.buildValueParameter( + context: VisitingContext +): KmValueParameter = KmValueParameter( + flags = valueParameterFlags(), + name = name.asString() +).also { parameter -> + annotations.mapTo(parameter.annotations) { it.buildAnnotation() } + parameter.type = returnType.buildType(context) + varargElementType?.let { varargElementType -> + parameter.varargElementType = varargElementType.buildType(context) + } +} + +private fun List.buildTypeParameters( + context: VisitingContext, + output: MutableList +) { + mapIndexedTo(output) { index, cirTypeParameter -> + KmTypeParameter( + flags = cirTypeParameter.typeParameterFlags(), + name = cirTypeParameter.name.asString(), + id = context.typeParameterIndexOffset + index, + variance = cirTypeParameter.variance.buildVariance() + ).also { parameter -> + cirTypeParameter.upperBounds.mapTo(parameter.upperBounds) { it.buildType(context) } + cirTypeParameter.annotations.mapTo(parameter.annotations) { it.buildAnnotation() } + } + } +} + +private fun CirType.buildType( + context: VisitingContext, + expansion: TypeAliasExpansion = FOR_TOP_LEVEL_TYPE +): KmType = when (this) { + is CirClassType -> buildType(context, expansion) + is CirTypeAliasType -> buildType(context, expansion) + is CirTypeParameterType -> buildType() + is CirFlexibleType -> { + lowerBound.buildType(context, expansion).also { + it.flexibleTypeUpperBound = KmFlexibleTypeUpperBound( + type = upperBound.buildType(context, expansion), + typeFlexibilityId = DynamicTypeDeserializer.id + ) + } + } +} + +private fun CirTypeParameterType.buildType(): KmType = + KmType(typeFlags()).also { type -> + type.classifier = KmClassifier.TypeParameter(index) + } + +private fun CirClassType.buildType( + context: VisitingContext, + expansion: TypeAliasExpansion +): KmType = KmType(typeFlags()).also { type -> + type.classifier = KmClassifier.Class(classifierId.asString()) + arguments.mapTo(type.arguments) { it.buildArgument(context, expansion) } + outerType?.let { type.outerType = it.buildType(context, expansion) } +} + +private fun CirTypeAliasType.buildType( + context: VisitingContext, + expansion: TypeAliasExpansion +): KmType = when (expansion) { + ONLY_ABBREVIATIONS -> buildAbbreviationType(context, expansion) + EXPANDED_WITHOUT_ABBREVIATIONS -> buildExpandedType(context, expansion) + FOR_TOP_LEVEL_TYPE -> buildExpandedType(context, EXPANDED_WITHOUT_ABBREVIATIONS).apply { + abbreviatedType = buildAbbreviationType(context, expansion) + } + FOR_NESTED_TYPE -> buildExpandedType(context, expansion).apply { + abbreviatedType = buildAbbreviationType(context, expansion) + } +} + +private fun CirTypeAliasType.buildAbbreviationType( + context: VisitingContext, + expansion: TypeAliasExpansion +): KmType { + val abbreviationType = KmType(typeFlags()) + abbreviationType.classifier = KmClassifier.TypeAlias(classifierId.asString()) + arguments.mapTo(abbreviationType.arguments) { it.buildArgument(context, expansion) } + return abbreviationType +} + +@Suppress("UnnecessaryVariable") +private fun CirTypeAliasType.buildExpandedType( + context: VisitingContext, + expansion: TypeAliasExpansion +): KmType { + val cirExpandedType = computeExpandedType(underlyingType) + val expandedType = cirExpandedType.buildType(context, expansion) + return expandedType +} + +private fun CirTypeProjection.buildArgument( + context: VisitingContext, + expansion: TypeAliasExpansion +): KmTypeProjection { + val effectiveExpansion = if (expansion == FOR_TOP_LEVEL_TYPE) FOR_NESTED_TYPE else expansion + return when (this) { + CirStarTypeProjection -> KmTypeProjection.STAR + is CirTypeProjectionImpl -> KmTypeProjection( + variance = projectionKind.buildVariance(), + type = type.buildType(context, effectiveExpansion) + ) + } +} + +@Suppress("NOTHING_TO_INLINE") +private inline fun Variance.buildVariance() = when (this) { + Variance.INVARIANT -> KmVariance.INVARIANT + Variance.IN_VARIANCE -> KmVariance.IN + Variance.OUT_VARIANCE -> KmVariance.OUT +} + +@Suppress("SpellCheckingInspection") +private enum class TypeAliasExpansion { + ONLY_ABBREVIATIONS, + EXPANDED_WITHOUT_ABBREVIATIONS, + FOR_TOP_LEVEL_TYPE, + FOR_NESTED_TYPE +} + +private val SETTER_VALUE_NAME = Name.identifier("value") diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/context.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/context.kt new file mode 100644 index 00000000000..d2abb16131c --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/context.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2010-2020 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.descriptors.commonizer.metadata + +import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.cir.* +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirNode +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirNode.Companion.indexOfCommon +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirNodeWithLiftingUp +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirRootNode + +internal interface VisitingContext { + val targetIndex: Int + val target: CommonizerTarget + val isCommon: Boolean + val typeParameterIndexOffset: Int + val topLevelContext: VisitingContext + + fun childContext(declarationWithTypeParameters: CirHasTypeParameters): VisitingContext { + val ownTypeParametersCount = declarationWithTypeParameters.typeParameters.size + return if (ownTypeParametersCount == 0) this else ChildVisitingContext(this, ownTypeParametersCount) + } + + companion object { + fun newContext(rootNode: CirRootNode, targetIndex: Int): VisitingContext = + TopLevelVisitingContext(rootNode, targetIndex) + } + + private class TopLevelVisitingContext( + rootNode: CirRootNode, + override val targetIndex: Int + ) : VisitingContext { + override val isCommon = rootNode.indexOfCommon == targetIndex + override val target = get(rootNode)!!.target + override val typeParameterIndexOffset get() = 0 + override val topLevelContext: VisitingContext get() = this + } + + private class ChildVisitingContext( + private val parent: VisitingContext, + typeParametersCount: Int + ) : VisitingContext by parent { + override val typeParameterIndexOffset = parent.typeParameterIndexOffset + typeParametersCount + } +} + +internal inline fun VisitingContext.get(node: CirNodeWithLiftingUp<*, *>): T? { + return when { + isCommon -> node.commonDeclaration() as T? + node.isLiftedUp -> null + else -> node.targetDeclarations[targetIndex] as T? + } +} + +internal inline fun VisitingContext.get(node: CirNode<*, *>): T? { + return (if (isCommon) node.commonDeclaration() else node.targetDeclarations[targetIndex]) as T? +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/flags.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/flags.kt new file mode 100644 index 00000000000..8b4a1456c30 --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/flags.kt @@ -0,0 +1,179 @@ +/* + * Copyright 2010-2020 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.descriptors.commonizer.metadata + +import kotlinx.metadata.Flag +import kotlinx.metadata.Flags +import kotlinx.metadata.flagsOf +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.commonizer.cir.* +import org.jetbrains.kotlin.resolve.constants.NullValue + +internal const val NO_FLAGS: Flags = 0 + +internal fun CirFunction.functionFlags(isExpect: Boolean): Flags = + flagsOfNotNull( + hasAnnotationsFlag, + visibilityFlag, + modalityFlag, + memberKindFlag, + Flag.Function.HAS_NON_STABLE_PARAMETER_NAMES.takeIf { !hasStableParameterNames }, + Flag.Function.IS_EXPECT.takeIf { isExpect } + ) or modifiers.modifiersFlags + +internal fun CirProperty.propertyFlags(isExpect: Boolean): Flags = + flagsOfNotNull( + hasAnnotationsFlag, + visibilityFlag, + modalityFlag, + memberKindFlag, + Flag.Property.HAS_GETTER.takeIf { getter != null }, + Flag.Property.HAS_SETTER.takeIf { setter != null }, + Flag.Property.IS_DELEGATED.takeIf { isDelegate }, + Flag.Property.IS_EXPECT.takeIf { isExpect } + ) or modifiersFlags + +internal fun CirPropertyAccessor.propertyAccessorFlags( + visibilityHolder: CirHasVisibility, + modalityHolder: CirHasModality +): Flags { + return flagsOfNotNull( + hasAnnotationsFlag, + visibilityHolder.visibilityFlag, + modalityHolder.modalityFlag, + Flag.PropertyAccessor.IS_NOT_DEFAULT.takeIf { !isDefault }, + Flag.PropertyAccessor.IS_EXTERNAL.takeIf { isExternal }, + Flag.PropertyAccessor.IS_INLINE.takeIf { isInline } + ) +} + +internal fun CirClassConstructor.classConstructorFlags(): Flags = + flagsOfNotNull( + hasAnnotationsFlag, + visibilityFlag, + Flag.Constructor.IS_SECONDARY.takeIf { !isPrimary }, + Flag.Constructor.HAS_NON_STABLE_PARAMETER_NAMES.takeIf { !hasStableParameterNames } + ) + +internal fun CirType.typeFlags(): Flags = + flagsOfNotNull( + nullableFlag, + //Flag.Type.IS_SUSPEND.takeIf { false } + ) + +internal fun CirTypeParameter.typeParameterFlags(): Flags = + flagsOfNotNull( + Flag.TypeParameter.IS_REIFIED.takeIf { isReified } + ) + +internal fun CirValueParameter.valueParameterFlags(): Flags = + flagsOfNotNull( + hasAnnotationsFlag, + Flag.ValueParameter.DECLARES_DEFAULT_VALUE.takeIf { declaresDefaultValue }, + Flag.ValueParameter.IS_CROSSINLINE.takeIf { isCrossinline }, + Flag.ValueParameter.IS_NOINLINE.takeIf { isNoinline } + ) + +internal fun CirClass.classFlags(isExpect: Boolean): Flags = + flagsOfNotNull( + hasAnnotationsFlag, + visibilityFlag, + modalityFlag, + classKindFlag, + Flag.Class.IS_COMPANION_OBJECT.takeIf { isCompanion }, + Flag.Class.IS_INNER.takeIf { isInner }, + Flag.Class.IS_DATA.takeIf { isData }, + Flag.Class.IS_EXTERNAL.takeIf { isExternal }, + Flag.Class.IS_EXPECT.takeIf { isExpect }, + Flag.Class.IS_INLINE.takeIf { isInline }, + //Flag.Class.IS_FUN.takeIf { false } + ) + +internal fun CirTypeAlias.typeAliasFlags(): Flags = + flagsOfNotNull( + hasAnnotationsFlag, + visibilityFlag + ) + +private inline val CirHasAnnotations.hasAnnotationsFlag: Flag? + get() = if (annotations.isNotEmpty()) Flag.Common.HAS_ANNOTATIONS else null + +private inline val CirHasVisibility.visibilityFlag: Flag + get() = when (visibility) { + DescriptorVisibilities.PUBLIC -> Flag.Common.IS_PUBLIC + DescriptorVisibilities.PROTECTED -> Flag.Common.IS_PROTECTED + DescriptorVisibilities.INTERNAL -> Flag.Common.IS_INTERNAL + DescriptorVisibilities.PRIVATE -> Flag.Common.IS_PRIVATE + else -> error("Unexpected visibility: $this") + } + +private inline val CirHasModality.modalityFlag: Flag + get() = when (modality) { + Modality.FINAL -> Flag.Common.IS_FINAL + Modality.ABSTRACT -> Flag.Common.IS_ABSTRACT + Modality.OPEN -> Flag.Common.IS_OPEN + Modality.SEALED -> Flag.Common.IS_SEALED + } + +private inline val CirFunction.memberKindFlag: Flag + get() = when (kind) { + CallableMemberDescriptor.Kind.DECLARATION -> Flag.Function.IS_DECLARATION + CallableMemberDescriptor.Kind.FAKE_OVERRIDE -> Flag.Function.IS_FAKE_OVERRIDE + CallableMemberDescriptor.Kind.DELEGATION -> Flag.Function.IS_DELEGATION + CallableMemberDescriptor.Kind.SYNTHESIZED -> Flag.Function.IS_SYNTHESIZED + } + +private inline val CirProperty.memberKindFlag: Flag + get() = when (kind) { + CallableMemberDescriptor.Kind.DECLARATION -> Flag.Property.IS_DECLARATION + CallableMemberDescriptor.Kind.FAKE_OVERRIDE -> Flag.Property.IS_FAKE_OVERRIDE + CallableMemberDescriptor.Kind.DELEGATION -> Flag.Property.IS_DELEGATION + CallableMemberDescriptor.Kind.SYNTHESIZED -> Flag.Property.IS_SYNTHESIZED + } + +private inline val CirClass.classKindFlag: Flag + get() = when (kind) { + ClassKind.CLASS -> Flag.Class.IS_CLASS + ClassKind.INTERFACE -> Flag.Class.IS_INTERFACE + ClassKind.ENUM_CLASS -> Flag.Class.IS_ENUM_CLASS + ClassKind.ENUM_ENTRY -> Flag.Class.IS_ENUM_ENTRY + ClassKind.ANNOTATION_CLASS -> Flag.Class.IS_ANNOTATION_CLASS + ClassKind.OBJECT -> Flag.Class.IS_OBJECT + } + +private inline val CirFunctionModifiers.modifiersFlags: Flags + get() = flagsOfNotNull( + Flag.Function.IS_OPERATOR.takeIf { isOperator }, + Flag.Function.IS_INFIX.takeIf { isInfix }, + Flag.Function.IS_INLINE.takeIf { isInline }, + Flag.Function.IS_TAILREC.takeIf { isTailrec }, + Flag.Function.IS_SUSPEND.takeIf { isSuspend }, + Flag.Function.IS_EXTERNAL.takeIf { isExternal } + ) + +private inline val CirProperty.modifiersFlags: Flags + get() = flagsOfNotNull( + Flag.Property.IS_VAR.takeIf { isVar }, + Flag.Property.IS_CONST.takeIf { isConst }, + Flag.Property.HAS_CONSTANT.takeIf { compileTimeInitializer.takeIf { it !is NullValue } != null }, + Flag.Property.IS_LATEINIT.takeIf { isLateInit }, + Flag.Property.IS_EXTERNAL.takeIf { isExternal } + ) + +private inline val CirType.nullableFlag: Flag? + get() { + val isNullable = when (this) { + is CirSimpleType -> isMarkedNullable + is CirFlexibleType -> lowerBound.isMarkedNullable + } + + return if (isNullable) Flag.Type.IS_NULLABLE else null + } + +private fun flagsOfNotNull(vararg flags: Flag?): Flags = flagsOf(*listOfNotNull(*flags).toTypedArray()) diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/visitor.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/visitor.kt new file mode 100644 index 00000000000..a9da38596aa --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/visitor.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2010-2020 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.descriptors.commonizer.metadata + +import kotlinx.metadata.* +import kotlinx.metadata.klib.KlibModuleMetadata +import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.cir.* +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.* +import org.jetbrains.kotlin.utils.addToStdlib.cast + +// TODO: add logging +@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") +internal object MetadataBuilder { + fun build(node: CirRootNode, targetIndex: Int): Pair> = + node.accept(MetadataBuildingVisitor(), VisitingContext.newContext(node, targetIndex)).cast() + + private class MetadataBuildingVisitor : CirNodeVisitor { + private val classConsumer = ClassConsumer() + + override fun visitRootNode(node: CirRootNode, context: VisitingContext): Pair> { + val modules: Collection = buildMembers(context, node.modules) + return context.target to modules + } + + override fun visitModuleNode(node: CirModuleNode, context: VisitingContext): KlibModuleMetadata? { + val cirModule = context.get(node) ?: return null + + val fragments: MutableCollection = mutableListOf() + buildMembers(context, node.packages, destination = fragments) + addEmptyFragments(fragments) + + return cirModule.buildModule(fragments) + } + + override fun visitPackageNode(node: CirPackageNode, context: VisitingContext): KmModuleFragment? { + val cirPackage = context.get(node) ?: return null + try { + buildMembers(context, node.classes, callback = classConsumer::consumeAll) + + val topLevelTypeAliases = mutableListOf() + node.typeAliases.values.forEach { typeAliasNode -> + when (val classifier = typeAliasNode.accept(this, context)) { + null -> Unit + is KmClass -> classConsumer.consume(classifier) + is KmTypeAlias -> topLevelTypeAliases += classifier + else -> error("Unexpected classifier: ${classifier::class.java}, $classifier") + } + } + + linkSealedClassesWithSubclasses(node.fqName, classConsumer) + + val topLevelFunctions: Collection = buildMembers(context, node.functions) + val topLevelProperties: Collection = buildMembers(context, node.properties) + + return cirPackage.buildModuleFragment(classConsumer.allClasses, topLevelTypeAliases, topLevelFunctions, topLevelProperties) + } finally { + // Important: clean-up class consumer every time when leaving package + classConsumer.reset() + } + } + + override fun visitPropertyNode(node: CirPropertyNode, context: VisitingContext): KmProperty? { + return context.get(node)?.buildProperty(context) + } + + override fun visitFunctionNode(node: CirFunctionNode, context: VisitingContext): KmFunction? { + return context.get(node)?.buildFunction(context) + } + + override fun visitClassNode(node: CirClassNode, context: VisitingContext): KmClass? { + val cirClass = context.get(node) ?: return null + + @Suppress("NAME_SHADOWING") val context = if (cirClass.isInner) context else context.topLevelContext + val classContext = context.childContext(cirClass) + + val directNestedClasses = buildMembers(context = classContext, node.classes, callback = classConsumer::consumeAll) + val nestedConstructors: Collection = buildMembers(context = classContext, node.constructors) + val nestedFunctions: Collection = buildMembers(context = classContext, node.functions) + val nestedProperties: Collection = buildMembers(context = classContext, node.properties) + + val className = node.classId.asString() + + return cirClass.buildClass(context, className, directNestedClasses, nestedConstructors, nestedFunctions, nestedProperties) + } + + override fun visitClassConstructorNode(node: CirClassConstructorNode, context: VisitingContext): KmConstructor? { + return context.get(node)?.buildClassConstructor(context) + } + + override fun visitTypeAliasNode(node: CirTypeAliasNode, context: VisitingContext): Any? { + val cirClassifier = context.get(node) ?: return null + val className = node.classId.asString() + + return when (cirClassifier) { + is CirTypeAlias -> cirClassifier.buildTypeAlias(context) + is CirClass -> cirClassifier.buildClass(context, className, emptyList(), emptyList(), emptyList(), emptyList()) + else -> error("Unexpected CIR classifier: ${cirClassifier::class.java}, $cirClassifier") + } + } + + private inline fun , reified M : Any> buildMembers( + context: VisitingContext, + cirMembersNodes: Map<*, N>, + destination: MutableCollection = mutableListOf(), + callback: (Collection) -> Unit = {} + ): Collection { + return cirMembersNodes.values.mapNotNullTo(destination) { it.accept(this, context) as M? }.also(callback) + } + } +} + +internal class ClassConsumer { + private val _allClasses = mutableListOf() + private val _sealedClasses = mutableListOf() + + val allClasses: Collection get() = _allClasses + val sealedClasses: Collection get() = _sealedClasses + + fun consume(clazz: KmClass) { + _allClasses += clazz + if (Flag.Common.IS_SEALED(clazz.flags)) _sealedClasses += clazz + } + + fun consumeAll(classes: Collection) = classes.forEach(::consume) + + fun reset() { + _allClasses.clear() + _sealedClasses.clear() + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt index 05b3c364828..3b91f0b733c 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt @@ -16,6 +16,14 @@ import org.jetbrains.kotlin.serialization.konan.impl.ForwardDeclarationsFqNames internal val DEPRECATED_ANNOTATION_FQN: FqName = FqName(Deprecated::class.java.name).intern() internal val DEPRECATED_ANNOTATION_CID: ClassId = internedClassId(DEPRECATED_ANNOTATION_FQN) +internal val ANY_CID: ClassId = internedClassId(StandardNames.FqNames.any.toSafe().intern()) +private val NOTHING_CID: ClassId = internedClassId(StandardNames.FqNames.nothing.toSafe().intern()) + +internal val SPECIAL_CLASS_WITHOUT_SUPERTYPES_CIDS = listOf( + ANY_CID, + NOTHING_CID +) + private val STANDARD_KOTLIN_PACKAGES = listOf( StandardNames.BUILT_INS_PACKAGE_FQ_NAME.asString(), "kotlinx"