diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirAnnotationFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirAnnotationFactory.kt index 37f047d945e..55e02a73aae 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirAnnotationFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirAnnotationFactory.kt @@ -6,12 +6,14 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory import gnu.trove.THashMap +import kotlinx.metadata.Flag +import kotlinx.metadata.Flags +import kotlinx.metadata.KmAnnotation +import kotlinx.metadata.KmAnnotationArgument import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.descriptors.commonizer.cir.CirAnnotation -import org.jetbrains.kotlin.descriptors.commonizer.cir.CirClassType -import org.jetbrains.kotlin.descriptors.commonizer.cir.CirConstantValue -import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName +import org.jetbrains.kotlin.descriptors.commonizer.cir.* import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirAnnotationImpl +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirProvided import org.jetbrains.kotlin.descriptors.commonizer.utils.* import org.jetbrains.kotlin.descriptors.commonizer.utils.compact import org.jetbrains.kotlin.name.Name @@ -50,6 +52,59 @@ object CirAnnotationFactory { ) } + fun createAnnotations(flags: Flags, typeResolver: CirTypeResolver, annotations: () -> List): List { + return if (!Flag.Common.HAS_ANNOTATIONS(flags)) + emptyList() + else + annotations().compactMap { create(it, typeResolver) } + } + + fun create(source: KmAnnotation, typeResolver: CirTypeResolver): CirAnnotation { + val classId = CirEntityId.create(source.className) + val clazz: CirProvided.Class = typeResolver.resolveClassifier(classId) + + val type = CirTypeFactory.createClassType( + classId = classId, + outerType = null, // annotation class can't be inner class + visibility = clazz.visibility, + arguments = clazz.typeParameters.compactMap { typeParameter -> + CirTypeProjectionImpl( + projectionKind = typeParameter.variance, + type = CirTypeFactory.createTypeParameterType( + index = typeParameter.index, + isMarkedNullable = false + ) + ) + }, + isMarkedNullable = false + ) + + val allValueArguments: Map> = source.arguments + if (allValueArguments.isEmpty()) + return create(type = type, constantValueArguments = emptyMap(), annotationValueArguments = emptyMap()) + + val constantValueArguments: MutableMap> = THashMap(allValueArguments.size) + val annotationValueArguments: MutableMap = THashMap(allValueArguments.size) + + allValueArguments.forEach { (name, constantValue) -> + val cirName = CirName.create(name) + if (constantValue is KmAnnotationArgument.AnnotationValue) + annotationValueArguments[cirName] = create(source = constantValue.value, typeResolver) + else + constantValueArguments[cirName] = CirConstantValueFactory.createSafely( + constantValue = constantValue, + constantName = cirName, + owner = source, + ) + } + + return create( + type = type, + constantValueArguments = constantValueArguments.compact(), + annotationValueArguments = annotationValueArguments.compact() + ) + } + fun create( type: CirClassType, constantValueArguments: Map>, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirClassConstructorFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirClassConstructorFactory.kt index d25de09ffdc..56b29282424 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirClassConstructorFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirClassConstructorFactory.kt @@ -5,11 +5,15 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.Flag +import kotlinx.metadata.KmConstructor +import kotlinx.metadata.klib.annotations import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.commonizer.cir.* import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirClassConstructorImpl +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeVisibility import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMap import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMapNotNull @@ -33,6 +37,18 @@ object CirClassConstructorFactory { ) } + fun create(source: KmConstructor, containingClass: CirContainingClass, typeResolver: CirTypeResolver): CirClassConstructor { + return create( + annotations = CirAnnotationFactory.createAnnotations(source.flags, typeResolver, source::annotations), + typeParameters = emptyList(), // TODO: nowhere to read constructor type parameters from + visibility = decodeVisibility(source.flags), + containingClass = containingClass, + valueParameters = source.valueParameters.compactMap { CirValueParameterFactory.create(it, typeResolver) }, + hasStableParameterNames = !Flag.Constructor.HAS_NON_STABLE_PARAMETER_NAMES(source.flags), + isPrimary = !Flag.Constructor.IS_SECONDARY(source.flags) + ) + } + @Suppress("NOTHING_TO_INLINE") inline fun create( annotations: List, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirClassFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirClassFactory.kt index 6bec4463b00..2410d537265 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirClassFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirClassFactory.kt @@ -5,15 +5,16 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.descriptors.DescriptorVisibility -import org.jetbrains.kotlin.descriptors.commonizer.cir.CirAnnotation -import org.jetbrains.kotlin.descriptors.commonizer.cir.CirClass -import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName -import org.jetbrains.kotlin.descriptors.commonizer.cir.CirTypeParameter +import kotlinx.metadata.Flag +import kotlinx.metadata.KmAnnotation +import kotlinx.metadata.KmClass +import kotlinx.metadata.klib.annotations +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.commonizer.cir.* import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirClassImpl +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeClassKind +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeModality +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeVisibility import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMap import org.jetbrains.kotlin.descriptors.commonizer.utils.filteredSupertypes import org.jetbrains.kotlin.resolve.isInlineClass @@ -36,6 +37,53 @@ object CirClassFactory { setSupertypes(source.filteredSupertypes.compactMap { CirTypeFactory.create(it) }) } + fun create(name: CirName, source: KmClass, typeResolver: CirTypeResolver): CirClass = create( + annotations = CirAnnotationFactory.createAnnotations(source.flags, typeResolver, source::annotations), + name = name, + typeParameters = source.typeParameters.compactMap { CirTypeParameterFactory.create(it, typeResolver) }, + visibility = decodeVisibility(source.flags), + modality = decodeModality(source.flags), + kind = decodeClassKind(source.flags), + companion = source.companionObject?.let(CirName::create), + isCompanion = Flag.Class.IS_COMPANION_OBJECT(source.flags), + isData = Flag.Class.IS_DATA(source.flags), + isInline = Flag.Class.IS_INLINE(source.flags), + isInner = Flag.Class.IS_INNER(source.flags), + isExternal = Flag.Class.IS_EXTERNAL(source.flags) + ).apply { + setSupertypes(source.filteredSupertypes.compactMap { CirTypeFactory.create(it, typeResolver) }) + } + + fun createDefaultEnumEntry( + name: CirName, + annotations: List, + enumClassId: CirEntityId, + enumClass: KmClass, + typeResolver: CirTypeResolver + ): CirClass = create( + annotations = annotations.compactMap { CirAnnotationFactory.create(it, typeResolver) }, + name = name, + typeParameters = emptyList(), + visibility = DescriptorVisibilities.PUBLIC, + modality = Modality.FINAL, + kind = ClassKind.ENUM_ENTRY, + companion = null, + isCompanion = false, + isData = false, + isInline = false, + isInner = false, + isExternal = false + ).apply { + val enumClassType = CirTypeFactory.createClassType( + classId = enumClassId, + outerType = null, + visibility = decodeVisibility(enumClass.flags), + arguments = emptyList(), + isMarkedNullable = false + ) + setSupertypes(listOf(enumClassType)) + } + @Suppress("NOTHING_TO_INLINE") inline fun create( annotations: List, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirConstantValueFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirConstantValueFactory.kt index 250ffa871af..3ef51226e11 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirConstantValueFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirConstantValueFactory.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.KmAnnotationArgument import org.jetbrains.kotlin.descriptors.commonizer.cir.CirConstantValue import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName @@ -25,6 +26,8 @@ object CirConstantValueFactory { constantValue: ConstantValue<*>, location: () -> String ): CirConstantValue<*> = when (constantValue) { + is NullValue -> CirConstantValue.NullValue + is StringValue -> CirConstantValue.StringValue(constantValue.value) is CharValue -> CirConstantValue.CharValue(constantValue.value) @@ -46,7 +49,6 @@ object CirConstantValueFactory { CirEntityId.create(constantValue.enumClassId), CirName.create(constantValue.enumEntryName) ) - is NullValue -> CirConstantValue.NullValue is ArrayValue -> CirConstantValue.ArrayValue( constantValue.value.compactMapIndexed { index, innerConstantValue -> @@ -54,8 +56,58 @@ object CirConstantValueFactory { constantValue = innerConstantValue, location = { "${location()}[$index]" } ) - }) + } + ) else -> error("Unsupported const value type: ${constantValue::class.java}, $constantValue at ${location()}") } + + fun createSafely( + constantValue: KmAnnotationArgument<*>?, + constantName: CirName? = null, + owner: Any, + ): CirConstantValue<*> = createSafely( + constantValue = constantValue, + location = { "${owner::class.java}, $owner" + constantName?.toString()?.let { "[$it]" } } + ) + + private fun createSafely( + constantValue: KmAnnotationArgument<*>?, + location: () -> String + ): CirConstantValue<*> = when (constantValue) { + null -> CirConstantValue.NullValue + + is KmAnnotationArgument.StringValue -> CirConstantValue.StringValue(constantValue.value) + is KmAnnotationArgument.CharValue -> CirConstantValue.CharValue(constantValue.value) + + is KmAnnotationArgument.ByteValue -> CirConstantValue.ByteValue(constantValue.value) + is KmAnnotationArgument.ShortValue -> CirConstantValue.ShortValue(constantValue.value) + is KmAnnotationArgument.IntValue -> CirConstantValue.IntValue(constantValue.value) + is KmAnnotationArgument.LongValue -> CirConstantValue.LongValue(constantValue.value) + + is KmAnnotationArgument.UByteValue -> CirConstantValue.UByteValue(constantValue.value) + is KmAnnotationArgument.UShortValue -> CirConstantValue.UShortValue(constantValue.value) + is KmAnnotationArgument.UIntValue -> CirConstantValue.UIntValue(constantValue.value) + is KmAnnotationArgument.ULongValue -> CirConstantValue.ULongValue(constantValue.value) + + is KmAnnotationArgument.FloatValue -> CirConstantValue.FloatValue(constantValue.value) + is KmAnnotationArgument.DoubleValue -> CirConstantValue.DoubleValue(constantValue.value) + is KmAnnotationArgument.BooleanValue -> CirConstantValue.BooleanValue(constantValue.value) + + is KmAnnotationArgument.EnumValue -> CirConstantValue.EnumValue( + CirEntityId.create(constantValue.enumClassName), + CirName.create(constantValue.enumEntryName) + ) + + is KmAnnotationArgument.ArrayValue -> CirConstantValue.ArrayValue( + constantValue.value.compactMapIndexed { index, innerConstantValue -> + createSafely( + constantValue = innerConstantValue, + location = { "${location()}[$index]" } + ) + } + ) + + else -> error("Unsupported annotation argument type: ${constantValue::class.java}, $constantValue") + } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirExtensionReceiverFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirExtensionReceiverFactory.kt index c8ae23fac71..24f9eb2876f 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirExtensionReceiverFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirExtensionReceiverFactory.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.KmType import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.commonizer.cir.CirAnnotation import org.jetbrains.kotlin.descriptors.commonizer.cir.CirExtensionReceiver @@ -18,6 +19,11 @@ object CirExtensionReceiverFactory { type = CirTypeFactory.create(source.type) ) + fun create(receiverParameterType: KmType, typeResolver: CirTypeResolver): CirExtensionReceiver = create( + annotations = emptyList(), // TODO nowhere to read receiver annotations from, see KT-42490 + type = CirTypeFactory.create(receiverParameterType, typeResolver) + ) + @Suppress("NOTHING_TO_INLINE") inline fun create( annotations: List, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirFunctionFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirFunctionFactory.kt index 1b22cc3e3b9..349319cdc70 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirFunctionFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirFunctionFactory.kt @@ -5,9 +5,15 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.Flag +import kotlinx.metadata.KmFunction +import kotlinx.metadata.klib.annotations import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.commonizer.cir.* import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirFunctionImpl +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeCallableKind +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeModality +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeVisibility import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMap object CirFunctionFactory { @@ -26,6 +32,23 @@ object CirFunctionFactory { modifiers = CirFunctionModifiersFactory.create(source), ) + fun create(name: CirName, source: KmFunction, containingClass: CirContainingClass?, typeResolver: CirTypeResolver): CirFunction { + return create( + annotations = CirAnnotationFactory.createAnnotations(source.flags, typeResolver, source::annotations), + name = name, + typeParameters = source.typeParameters.compactMap { CirTypeParameterFactory.create(it, typeResolver) }, + visibility = decodeVisibility(source.flags), + modality = decodeModality(source.flags), + containingClass = containingClass, + valueParameters = source.valueParameters.compactMap { CirValueParameterFactory.create(it, typeResolver) }, + hasStableParameterNames = !Flag.Function.HAS_NON_STABLE_PARAMETER_NAMES(source.flags), + extensionReceiver = source.receiverParameterType?.let { CirExtensionReceiverFactory.create(it, typeResolver) }, + returnType = CirTypeFactory.create(source.returnType, typeResolver), + kind = decodeCallableKind(source.flags), + modifiers = CirFunctionModifiersFactory.create(source), + ) + } + @Suppress("NOTHING_TO_INLINE") inline fun create( annotations: List, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirFunctionModifiersFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirFunctionModifiersFactory.kt index af25846e8a2..48b70ce298f 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirFunctionModifiersFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirFunctionModifiersFactory.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.Flag +import kotlinx.metadata.KmFunction import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.commonizer.cir.CirFunctionModifiers import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirFunctionModifiersImpl @@ -22,6 +24,15 @@ object CirFunctionModifiersFactory { isExternal = source.isExternal ) + fun create(source: KmFunction): CirFunctionModifiers = create( + isOperator = Flag.Function.IS_OPERATOR(source.flags), + isInfix = Flag.Function.IS_INFIX(source.flags), + isInline = Flag.Function.IS_INLINE(source.flags), + isTailrec = Flag.Function.IS_TAILREC(source.flags), + isSuspend = Flag.Function.IS_SUSPEND(source.flags), + isExternal = Flag.Function.IS_EXTERNAL(source.flags) + ) + fun create( isOperator: Boolean, isInfix: Boolean, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertyFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertyFactory.kt index 1a593a27a45..3c14889e766 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertyFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertyFactory.kt @@ -5,12 +5,19 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.Flag +import kotlinx.metadata.KmProperty +import kotlinx.metadata.klib.annotations +import kotlinx.metadata.klib.compileTimeValue import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.commonizer.cir.* import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirPropertyImpl +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeCallableKind +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeModality +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeVisibility import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMap object CirPropertyFactory { @@ -45,6 +52,37 @@ object CirPropertyFactory { ) } + fun create(name: CirName, source: KmProperty, containingClass: CirContainingClass?, typeResolver: CirTypeResolver): CirProperty { + val compileTimeInitializer = if (Flag.Property.HAS_CONSTANT(source.flags)) { + CirConstantValueFactory.createSafely( + constantValue = source.compileTimeValue, + owner = source, + ) + } else CirConstantValue.NullValue + + return create( + annotations = CirAnnotationFactory.createAnnotations(source.flags, typeResolver, source::annotations), + name = name, + typeParameters = source.typeParameters.compactMap { CirTypeParameterFactory.create(it, typeResolver) }, + visibility = decodeVisibility(source.flags), + modality = decodeModality(source.flags), + containingClass = containingClass, + isExternal = Flag.Property.IS_EXTERNAL(source.flags), + extensionReceiver = source.receiverParameterType?.let { CirExtensionReceiverFactory.create(it, typeResolver) }, + returnType = CirTypeFactory.create(source.returnType, typeResolver), + kind = decodeCallableKind(source.flags), + isVar = Flag.Property.IS_VAR(source.flags), + isLateInit = Flag.Property.IS_LATEINIT(source.flags), + isConst = Flag.Property.IS_CONST(source.flags), + isDelegate = Flag.Property.IS_DELEGATED(source.flags), + getter = CirPropertyGetterFactory.create(source, typeResolver), + setter = CirPropertySetterFactory.create(source, typeResolver), + backingFieldAnnotations = emptyList(), // TODO unclear where to read backing/delegate field annotations from, see KT-44625 + delegateFieldAnnotations = emptyList(), // TODO unclear where to read backing/delegate field annotations from, see KT-44625 + compileTimeInitializer = compileTimeInitializer + ) + } + @Suppress("NOTHING_TO_INLINE") inline fun create( annotations: List, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertyGetterFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertyGetterFactory.kt index 0ffd7ede42a..f5955ff505e 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertyGetterFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertyGetterFactory.kt @@ -5,6 +5,9 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.Flag +import kotlinx.metadata.KmProperty +import kotlinx.metadata.klib.getterAnnotations import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor import org.jetbrains.kotlin.descriptors.commonizer.cir.CirAnnotation import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPropertyGetter @@ -35,6 +38,26 @@ object CirPropertyGetterFactory { ) } + fun create(source: KmProperty, typeResolver: CirTypeResolver): CirPropertyGetter? { + if (!Flag.Property.HAS_GETTER(source.flags)) + return null + + val getterFlags = source.getterFlags + + val isDefault = !Flag.PropertyAccessor.IS_NOT_DEFAULT(getterFlags) + val annotations = CirAnnotationFactory.createAnnotations(getterFlags, typeResolver, source::getterAnnotations) + + if (isDefault && annotations.isEmpty()) + return DEFAULT_NO_ANNOTATIONS + + return create( + annotations = annotations, + isDefault = isDefault, + isExternal = Flag.PropertyAccessor.IS_EXTERNAL(getterFlags), + isInline = Flag.PropertyAccessor.IS_INLINE(getterFlags) + ) + } + fun create( annotations: List, isDefault: Boolean, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertySetterFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertySetterFactory.kt index faa47c53241..1e097985101 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertySetterFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirPropertySetterFactory.kt @@ -5,12 +5,17 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.Flag +import kotlinx.metadata.KmProperty +import kotlinx.metadata.klib.annotations +import kotlinx.metadata.klib.setterAnnotations import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.commonizer.cir.CirAnnotation import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPropertySetter import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirPropertySetterImpl +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeVisibility import org.jetbrains.kotlin.descriptors.commonizer.utils.Interner import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMap @@ -26,6 +31,24 @@ object CirPropertySetterFactory { isInline = source.isInline ) + fun create(source: KmProperty, typeResolver: CirTypeResolver): CirPropertySetter? { + if (!Flag.Property.HAS_SETTER(source.flags)) + return null + + val setterFlags = source.setterFlags + + return create( + annotations = CirAnnotationFactory.createAnnotations(setterFlags, typeResolver, source::setterAnnotations), + parameterAnnotations = source.setterParameter?.let { setterParameter -> + CirAnnotationFactory.createAnnotations(setterParameter.flags, typeResolver, setterParameter::annotations) + }.orEmpty(), + visibility = decodeVisibility(setterFlags), + isDefault = !Flag.PropertyAccessor.IS_NOT_DEFAULT(setterFlags), + isExternal = Flag.PropertyAccessor.IS_EXTERNAL(setterFlags), + isInline = Flag.PropertyAccessor.IS_INLINE(setterFlags) + ) + } + fun create( annotations: List, parameterAnnotations: List, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasExpander.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasExpander.kt new file mode 100644 index 00000000000..359ef087dcb --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasExpander.kt @@ -0,0 +1,181 @@ +/* + * 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.descriptors.commonizer.cir.factory + +import org.jetbrains.kotlin.descriptors.commonizer.cir.* +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirProvided +import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMapIndexed +import org.jetbrains.kotlin.types.Variance + +class CirTypeAliasExpansion( + val typeAlias: CirProvided.TypeAlias, + val arguments: List, + val isMarkedNullable: Boolean, + val typeResolver: CirTypeResolver +) { + companion object { + fun create( + typeAliasId: CirEntityId, + arguments: List, + isMarkedNullable: Boolean, + typeResolver: CirTypeResolver + ): CirTypeAliasExpansion { + val typeAlias: CirProvided.TypeAlias = typeResolver.resolveClassifier(typeAliasId) + checkArgumentsCount(typeAlias, typeAliasId, arguments) + + return CirTypeAliasExpansion( + typeAlias = typeAlias, + arguments = arguments, + isMarkedNullable = isMarkedNullable, + typeResolver = typeResolver + ) + } + } +} + +object CirTypeAliasExpander { + fun expand(expansion: CirTypeAliasExpansion): CirClassOrTypeAliasType { + val underlyingType = expansion.typeAlias.underlyingType + val underlyingProjection = CirProvided.RegularTypeProjection(Variance.INVARIANT, underlyingType) + + val expandedProjection = expandTypeProjection(expansion, underlyingProjection, Variance.INVARIANT) + check(expandedProjection is CirTypeProjectionImpl) { + "Type alias expansion: result for $underlyingType is $expandedProjection, should not be a star projection" + } + check(expandedProjection.projectionKind == Variance.INVARIANT) { + "Type alias expansion: result for $underlyingType is $expandedProjection, should be invariant" + } + + val expandedType = expandedProjection.type as CirClassOrTypeAliasType + return CirTypeFactory.makeNullableIfNecessary(expandedType, expansion.isMarkedNullable) + } + + private fun expandTypeProjection( + expansion: CirTypeAliasExpansion, + projection: CirProvided.TypeProjection, + typeParameterVariance: Variance + ): CirTypeProjection { + val type = when (projection) { + is CirProvided.StarTypeProjection -> return CirStarTypeProjection + is CirProvided.RegularTypeProjection -> projection.type + } + + val argument = when (type) { + is CirProvided.TypeParameterType -> expansion.arguments[type.index] + is CirProvided.TypeAliasType -> { + val substitutedType = expandTypeAliasType(expansion, type) + CirTypeProjectionImpl(projection.variance, substitutedType) + } + is CirProvided.ClassType -> { + val substitutedType = expandClassType(expansion, type) + CirTypeProjectionImpl(projection.variance, substitutedType) + } + } + + return when (argument) { + is CirStarTypeProjection -> CirStarTypeProjection + is CirTypeProjectionImpl -> { + val argumentType = argument.type as CirSimpleType + + val resultingVariance = run { + val argumentVariance = argument.projectionKind + val underlyingVariance = projection.variance + + val substitutionVariance = when { + underlyingVariance == argumentVariance -> argumentVariance + underlyingVariance == Variance.INVARIANT -> argumentVariance + argumentVariance == Variance.INVARIANT -> underlyingVariance + else -> argumentVariance + } + + when { + typeParameterVariance == substitutionVariance -> substitutionVariance + typeParameterVariance == Variance.INVARIANT -> substitutionVariance + substitutionVariance == Variance.INVARIANT -> Variance.INVARIANT + else -> substitutionVariance + } + } + + val substitutedType = CirTypeFactory.makeNullableIfNecessary(argumentType, type.isMarkedNullable) + + CirTypeProjectionImpl(resultingVariance, substitutedType) + } + } + } + + private fun expandTypeAliasType( + expansion: CirTypeAliasExpansion, + type: CirProvided.TypeAliasType + ): CirTypeAliasType { + val typeAlias: CirProvided.TypeAlias = expansion.typeResolver.resolveClassifier(type.typeAliasId) + checkArgumentsCount(typeAlias, type.typeAliasId, type.arguments) + + val expandedArguments = type.arguments.compactMapIndexed { index, argument -> + val projection = expandTypeProjection(expansion, argument, typeAlias.typeParameters[index].variance) + makeNullableTypeIfNecessary(projection, argument) + } + + val nestedExpansion = CirTypeAliasExpansion(typeAlias, expandedArguments, type.isMarkedNullable, expansion.typeResolver) + val nestedExpandedType = expand(nestedExpansion) + + return CirTypeFactory.createTypeAliasType( + typeAliasId = type.typeAliasId, + underlyingType = nestedExpandedType, + arguments = expandedArguments, + isMarkedNullable = type.isMarkedNullable + ) + } + + private fun expandClassType( + expansion: CirTypeAliasExpansion, + type: CirProvided.ClassType + ): CirClassType { + val clazz: CirProvided.Class = expansion.typeResolver.resolveClassifier(type.classId) + checkArgumentsCount(clazz, type.classId, type.arguments) + + val expandedArguments = type.arguments.compactMapIndexed { index, argument -> + val projection = expandTypeProjection(expansion, argument, clazz.typeParameters[index].variance) + makeNullableTypeIfNecessary(projection, argument) + } + + return CirTypeFactory.createClassType( + classId = type.classId, + outerType = type.outerType?.let { expandClassType(expansion, it) }, + visibility = clazz.visibility, + arguments = expandedArguments, + isMarkedNullable = type.isMarkedNullable + ) + } + + @Suppress("NOTHING_TO_INLINE") + private inline fun makeNullableTypeIfNecessary( + projection: CirTypeProjection, + originalArgument: CirProvided.TypeProjection + ): CirTypeProjection { + return when (projection) { + is CirStarTypeProjection -> CirStarTypeProjection + is CirTypeProjectionImpl -> { + val originalTypeIsNullable = (originalArgument as? CirProvided.RegularTypeProjection)?.type?.isMarkedNullable == true + if (!originalTypeIsNullable) + return projection + + val projectionType = projection.type as CirSimpleType + if (projectionType.isMarkedNullable) + return projection + + CirTypeProjectionImpl( + projectionKind = projection.projectionKind, + type = CirTypeFactory.makeNullable(projectionType) + ) + } + } + } +} + +private fun checkArgumentsCount(classifier: CirProvided.Classifier, classifierId: CirEntityId, arguments: List<*>) = + check(classifier.typeParameters.size == arguments.size) { + "${classifier::class.java.simpleName} $classifierId has different number of type parameters than the number of supplied arguments: ${classifier.typeParameters.size} != ${arguments.size}" + } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasFactory.kt index f2039d3d414..d445eea6412 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeAliasFactory.kt @@ -5,10 +5,12 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.KmTypeAlias import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.commonizer.cir.* import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirTypeAliasImpl +import org.jetbrains.kotlin.descriptors.commonizer.metadata.decodeVisibility import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMap object CirTypeAliasFactory { @@ -26,6 +28,20 @@ object CirTypeAliasFactory { ) } + fun create(name: CirName, source: KmTypeAlias, typeResolver: CirTypeResolver): CirTypeAlias { + val underlyingType = CirTypeFactory.create(source.underlyingType, typeResolver) as CirClassOrTypeAliasType + val expandedType = CirTypeFactory.unabbreviate(underlyingType) + + return create( + annotations = CirAnnotationFactory.createAnnotations(source.flags, typeResolver, source::annotations), + name = name, + typeParameters = source.typeParameters.compactMap { CirTypeParameterFactory.create(it, typeResolver) }, + visibility = decodeVisibility(source.flags), + underlyingType = underlyingType, + expandedType = expandedType + ) + } + @Suppress("NOTHING_TO_INLINE") inline fun create( annotations: List, 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 94150895f41..4c29b5b4313 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 @@ -5,11 +5,16 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import gnu.trove.TIntObjectHashMap +import kotlinx.metadata.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.commonizer.cir.* import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirClassTypeImpl import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirTypeAliasTypeImpl +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirProvided +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirProvidedClassifiers +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.TypeParameterResolver import org.jetbrains.kotlin.descriptors.commonizer.core.computeExpandedType import org.jetbrains.kotlin.descriptors.commonizer.utils.* import org.jetbrains.kotlin.types.* @@ -29,6 +34,56 @@ object CirTypeFactory { private val typeAliasTypeInterner = Interner() private val typeParameterTypeInterner = Interner() + fun create(source: KmType, typeResolver: CirTypeResolver): CirType { + @Suppress("NAME_SHADOWING") + val source = source.abbreviatedType ?: source + val isMarkedNullable = Flag.Type.IS_NULLABLE(source.flags) + + return when (val classifier = source.classifier) { + is KmClassifier.Class -> { + val classId = CirEntityId.create(classifier.name) + + val outerType = source.outerType?.let { outerType -> + val outerClassType = create(outerType, typeResolver) + check(outerClassType is CirClassType) { "Outer type of $classId is not a class: $outerClassType" } + outerClassType + } + + val clazz: CirProvided.Class = typeResolver.resolveClassifier(classId) + + createClassType( + classId = classId, + outerType = outerType, + visibility = clazz.visibility, + arguments = createArguments(source.arguments, typeResolver), + isMarkedNullable = isMarkedNullable + ) + } + is KmClassifier.TypeAlias -> { + val typeAliasId = CirEntityId.create(classifier.name) + + val arguments = createArguments(source.arguments, typeResolver) + + val underlyingType = CirTypeAliasExpander.expand( + CirTypeAliasExpansion.create(typeAliasId, arguments, isMarkedNullable, typeResolver) + ) + + createTypeAliasType( + typeAliasId = typeAliasId, + underlyingType = underlyingType, + arguments = arguments, + isMarkedNullable = isMarkedNullable + ) + } + is KmClassifier.TypeParameter -> { + createTypeParameterType( + index = typeResolver.resolveTypeParameterIndex(classifier.id), + isMarkedNullable = isMarkedNullable + ) + } + } + } + fun create(source: KotlinType): CirType = source.unwrap().run { when (this) { is SimpleType -> create(this) @@ -161,6 +216,13 @@ object CirTypeFactory { inline fun makeNullableIfNecessary(type: T, necessary: Boolean): T = if (!necessary) type else makeNullable(type) + @Suppress("NOTHING_TO_INLINE") + inline fun decodeVariance(variance: KmVariance): Variance = when (variance) { + KmVariance.INVARIANT -> Variance.INVARIANT + KmVariance.IN -> Variance.IN_VARIANCE + KmVariance.OUT -> Variance.OUT_VARIANCE + } + fun unabbreviate(type: CirClassOrTypeAliasType): CirClassType = when (type) { is CirClassType -> { var hasAbbreviationsInArguments = false @@ -239,6 +301,19 @@ object CirTypeFactory { ) } + @Suppress("NOTHING_TO_INLINE") + private inline fun createArguments(arguments: List, typeResolver: CirTypeResolver): List { + return arguments.compactMap { argument -> + val variance = argument.variance ?: return@compactMap CirStarTypeProjection + val argumentType = argument.type ?: return@compactMap CirStarTypeProjection + + CirTypeProjectionImpl( + projectionKind = decodeVariance(variance), + type = create(argumentType, typeResolver) + ) + } + } + private inline val TypeParameterDescriptor.typeParameterIndex: Int get() { var index = index @@ -257,3 +332,68 @@ object CirTypeFactory { return index } } + +typealias TypeParameterId = Int +typealias TypeParameterIndex = Int + +abstract class CirTypeResolver : TypeParameterResolver { + abstract val providedClassifiers: CirProvidedClassifiers + protected abstract val typeParameterIndexOffset: Int + + inline fun resolveClassifier(classifierId: CirEntityId): T { + val classifier = providedClassifiers.classifier(classifierId) + ?: error("Unresolved classifier: $classifierId") + + check(classifier is T) { + "Resolved classifier $classifierId of type ${classifier::class.java.simpleName}. Expected: ${T::class.java.simpleName}." + } + + return classifier + } + + abstract fun resolveTypeParameterIndex(id: TypeParameterId): TypeParameterIndex + abstract override fun resolveTypeParameter(id: TypeParameterId): KmTypeParameter + + private class TopLevel(override val providedClassifiers: CirProvidedClassifiers) : CirTypeResolver() { + override val typeParameterIndexOffset get() = 0 + + override fun resolveTypeParameterIndex(id: TypeParameterId) = error("Unresolved type parameter: id=$id") + override fun resolveTypeParameter(id: TypeParameterId) = error("Unresolved type parameter: id=$id") + } + + private class Nested( + private val parent: CirTypeResolver, + private val typeParameterMapping: TIntObjectHashMap + ) : CirTypeResolver() { + override val providedClassifiers get() = parent.providedClassifiers + override val typeParameterIndexOffset = typeParameterMapping.size() + parent.typeParameterIndexOffset + + override fun resolveTypeParameterIndex(id: TypeParameterId) = + typeParameterMapping[id]?.index ?: parent.resolveTypeParameterIndex(id) + + override fun resolveTypeParameter(id: TypeParameterId) = + typeParameterMapping[id]?.typeParameter ?: parent.resolveTypeParameter(id) + } + + private class TypeParameterInfo(val index: TypeParameterIndex, val typeParameter: KmTypeParameter) + + fun create(typeParameters: List): CirTypeResolver = + if (typeParameters.isEmpty()) + this + else { + val mapping = TIntObjectHashMap(typeParameters.size * 2) + typeParameters.forEachIndexed { localIndex, typeParameter -> + val typeParameterInfo = TypeParameterInfo( + index = localIndex + typeParameterIndexOffset, + typeParameter = typeParameter + ) + mapping.put(typeParameter.id, typeParameterInfo) + } + + Nested(this, mapping) + } + + companion object { + fun create(providedClassifiers: CirProvidedClassifiers): CirTypeResolver = TopLevel(providedClassifiers) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeParameterFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeParameterFactory.kt index 824627b9fcb..80494fb58e8 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeParameterFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirTypeParameterFactory.kt @@ -5,12 +5,17 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.Flag +import kotlinx.metadata.KmTypeParameter +import kotlinx.metadata.klib.annotations import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.commonizer.cir.CirAnnotation import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName import org.jetbrains.kotlin.descriptors.commonizer.cir.CirType import org.jetbrains.kotlin.descriptors.commonizer.cir.CirTypeParameter +import org.jetbrains.kotlin.descriptors.commonizer.cir.factory.CirTypeFactory.decodeVariance import org.jetbrains.kotlin.descriptors.commonizer.cir.impl.CirTypeParameterImpl +import org.jetbrains.kotlin.descriptors.commonizer.metadata.ALWAYS_HAS_ANNOTATIONS import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMap import org.jetbrains.kotlin.descriptors.commonizer.utils.filteredUpperBounds import org.jetbrains.kotlin.types.Variance @@ -24,6 +29,14 @@ object CirTypeParameterFactory { upperBounds = source.filteredUpperBounds.compactMap(CirTypeFactory::create) ) + fun create(source: KmTypeParameter, typeResolver: CirTypeResolver): CirTypeParameter = create( + annotations = CirAnnotationFactory.createAnnotations(ALWAYS_HAS_ANNOTATIONS, typeResolver, source::annotations), + name = CirName.create(source.name), + isReified = Flag.TypeParameter.IS_REIFIED(source.flags), + variance = decodeVariance(source.variance), + upperBounds = source.filteredUpperBounds.compactMap { CirTypeFactory.create(it, typeResolver) } + ) + @Suppress("NOTHING_TO_INLINE") inline fun create( annotations: List, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirValueParameterFactory.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirValueParameterFactory.kt index 1525a481e81..ba740e30603 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirValueParameterFactory.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cir/factory/CirValueParameterFactory.kt @@ -5,6 +5,9 @@ package org.jetbrains.kotlin.descriptors.commonizer.cir.factory +import kotlinx.metadata.Flag +import kotlinx.metadata.KmValueParameter +import kotlinx.metadata.klib.annotations import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.commonizer.cir.CirAnnotation import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName @@ -27,6 +30,16 @@ object CirValueParameterFactory { isNoinline = source.isNoinline ) + fun create(source: KmValueParameter, typeResolver: CirTypeResolver): CirValueParameter = create( + annotations = CirAnnotationFactory.createAnnotations(source.flags, typeResolver, source::annotations), + name = CirName.create(source.name), + returnType = CirTypeFactory.create(source.type!!, typeResolver), + varargElementType = source.varargElementType?.let { CirTypeFactory.create(it, typeResolver) }, + declaresDefaultValue = Flag.ValueParameter.DECLARES_DEFAULT_VALUE(source.flags), + isCrossinline = Flag.ValueParameter.IS_CROSSINLINE(source.flags), + isNoinline = Flag.ValueParameter.IS_NOINLINE(source.flags) + ) + fun create( annotations: List, name: CirName, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt index cfe4c416e41..016f788072b 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.Status import org.jetbrains.kotlin.descriptors.commonizer.core.CommonizationVisitor import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.* import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirNode.Companion.dimension -import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirTreeMerger.CirTreeMergeResult +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirTreeMergerV2.CirTreeMergeResult import org.jetbrains.kotlin.descriptors.commonizer.metadata.MetadataBuilder import org.jetbrains.kotlin.library.SerializedMetadata import org.jetbrains.kotlin.storage.LockBasedStorageManager @@ -46,7 +46,7 @@ private fun mergeAndCommonize(storageManager: StorageManager, parameters: Common CirProvidedClassifiers.by(parameters.dependencyModulesProvider) ) ) - val mergeResult = CirTreeMerger(storageManager, classifiers, parameters).merge() + val mergeResult = CirTreeMergerV2(storageManager, classifiers, parameters).build() // commonize: val mergedTree = mergeResult.root diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirFictitiousFunctionClassifiers.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirFictitiousFunctionClassifiers.kt index eb4a8699c86..9eb64612961 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirFictitiousFunctionClassifiers.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirFictitiousFunctionClassifiers.kt @@ -5,10 +5,12 @@ package org.jetbrains.kotlin.descriptors.commonizer.mergedtree -import gnu.trove.THashSet +import gnu.trove.THashMap +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPackageName +import org.jetbrains.kotlin.types.Variance object CirFictitiousFunctionClassifiers : CirProvidedClassifiers { private const val MIN_ARITY = 0 @@ -17,16 +19,28 @@ object CirFictitiousFunctionClassifiers : CirProvidedClassifiers { private val FUNCTION_PREFIXES = arrayOf("Function", "SuspendFunction") private val PACKAGE_NAME = CirPackageName.create("kotlin") - private val classifiers: Set = THashSet().apply { + private val classifiers: Map = THashMap().apply { (MIN_ARITY..MAX_ARITY).forEach { arity -> FUNCTION_PREFIXES.forEach { prefix -> - this += buildFictitiousFunctionClass(prefix, arity) + buildFictitiousFunctionClass(prefix, arity, this::set) } } } - override fun hasClassifier(classifierId: CirEntityId): Boolean = classifierId in classifiers + override fun hasClassifier(classifierId: CirEntityId) = classifierId in classifiers + override fun classifier(classifierId: CirEntityId): CirProvided.Class? = classifiers[classifierId] - private fun buildFictitiousFunctionClass(prefix: String, arity: Int): CirEntityId = - CirEntityId.create(PACKAGE_NAME, CirName.create("$prefix$arity")) + private inline fun buildFictitiousFunctionClass(prefix: String, arity: Int, consumer: (CirEntityId, CirProvided.Class) -> Unit) { + val typeParameters = List(arity + 1) { index -> + CirProvided.TypeParameter( + index = index, + variance = if (index == arity) Variance.OUT_VARIANCE else Variance.IN_VARIANCE + ) + } + + val classId = CirEntityId.create(PACKAGE_NAME, CirName.create("$prefix$arity")) + val clazz = CirProvided.Class(typeParameters, DescriptorVisibilities.PUBLIC) + + consumer(classId, clazz) + } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/classifierContainers.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirKnownClassifiers.kt similarity index 100% rename from native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/classifierContainers.kt rename to native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirKnownClassifiers.kt diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirProvidedClassifiers.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirProvidedClassifiers.kt index 46c9de53303..e19d59e3b86 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirProvidedClassifiers.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirProvidedClassifiers.kt @@ -5,22 +5,29 @@ package org.jetbrains.kotlin.descriptors.commonizer.mergedtree +import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId +import org.jetbrains.kotlin.types.Variance /** A set of classes and type aliases provided by libraries (either the libraries to commonize, or their dependency libraries)/ */ interface CirProvidedClassifiers { fun hasClassifier(classifierId: CirEntityId): Boolean - - // TODO: implement later - //fun classifier(classifierId: ClassId): Any? + fun classifier(classifierId: CirEntityId): CirProvided.Classifier? object EMPTY : CirProvidedClassifiers { override fun hasClassifier(classifierId: CirEntityId) = false + override fun classifier(classifierId: CirEntityId): CirProvided.Classifier? = null } private class CompositeClassifiers(val delegates: List) : CirProvidedClassifiers { override fun hasClassifier(classifierId: CirEntityId) = delegates.any { it.hasClassifier(classifierId) } + override fun classifier(classifierId: CirEntityId): CirProvided.Classifier? { + for (delegate in delegates) { + delegate.classifier(classifierId)?.let { return it } + } + return null + } } companion object { @@ -42,6 +49,55 @@ interface CirProvidedClassifiers { } fun by(modulesProvider: ModulesProvider?): CirProvidedClassifiers = - if (modulesProvider != null) CirProvidedClassifiersByModules(modulesProvider) else EMPTY + if (modulesProvider != null) CirProvidedClassifiersByModules.load(modulesProvider) else EMPTY } } + +object CirProvided { + /* Classifiers */ + sealed interface Classifier { + val typeParameters: List + } + + data class Class( + override val typeParameters: List, + val visibility: DescriptorVisibility + ) : Classifier + + data class TypeAlias( + override val typeParameters: List, + val underlyingType: Type + ) : Classifier + + /* Type parameter */ + data class TypeParameter(val index: Int, val variance: Variance) + + /* Types */ + sealed interface Type { + val isMarkedNullable: Boolean + } + + data class TypeParameterType( + val index: Int, + override val isMarkedNullable: Boolean + ) : Type + + data class ClassType( + val classId: CirEntityId, + val outerType: ClassType?, + val arguments: List, + override val isMarkedNullable: Boolean + ) : Type + + data class TypeAliasType( + val typeAliasId: CirEntityId, + val arguments: List, + override val isMarkedNullable: Boolean + ) : Type + + /* Type projections */ + sealed interface TypeProjection + object StarTypeProjection : TypeProjection + data class RegularTypeProjection(val variance: Variance, val type: Type) : TypeProjection +} + diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirProvidedClassifiersByModules.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirProvidedClassifiersByModules.kt index b28a1cb23b3..a9ed967f3fd 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirProvidedClassifiersByModules.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirProvidedClassifiersByModules.kt @@ -5,58 +5,257 @@ package org.jetbrains.kotlin.descriptors.commonizer.mergedtree -import gnu.trove.THashSet +import com.intellij.util.containers.FactoryMap +import gnu.trove.THashMap +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPackageName +import org.jetbrains.kotlin.descriptors.commonizer.utils.NON_EXISTING_CLASSIFIER_ID +import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMap +import org.jetbrains.kotlin.descriptors.commonizer.utils.compactMapIndexed +import org.jetbrains.kotlin.descriptors.commonizer.utils.isUnderKotlinNativeSyntheticPackages +import org.jetbrains.kotlin.library.SerializedMetadata import org.jetbrains.kotlin.library.metadata.parsePackageFragment import org.jetbrains.kotlin.metadata.ProtoBuf -import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl +import org.jetbrains.kotlin.metadata.deserialization.* +import org.jetbrains.kotlin.serialization.deserialization.ProtoEnumFlags +import org.jetbrains.kotlin.serialization.deserialization.descriptorVisibility +import org.jetbrains.kotlin.types.Variance -internal class CirProvidedClassifiersByModules(modulesProvider: ModulesProvider) : CirProvidedClassifiers { - private val classifiers: Set = loadClassifiers(modulesProvider) +internal class CirProvidedClassifiersByModules private constructor( + private val hasForwardDeclarations: Boolean, + private val classifiers: Map, +) : CirProvidedClassifiers { + override fun hasClassifier(classifierId: CirEntityId) = + if (classifierId.packageName.isUnderKotlinNativeSyntheticPackages) { + hasForwardDeclarations + } else { + classifierId in classifiers + } - override fun hasClassifier(classifierId: CirEntityId): Boolean = classifierId in classifiers -} + override fun classifier(classifierId: CirEntityId) = + if (classifierId.packageName.isUnderKotlinNativeSyntheticPackages) { + if (hasForwardDeclarations) FALLBACK_FORWARD_DECLARATION_CLASS else null + } else { + classifiers[classifierId] + } -private fun loadClassifiers(modulesProvider: ModulesProvider): Set { - val result = THashSet() + companion object { + fun load(modulesProvider: ModulesProvider): CirProvidedClassifiers { + val classifiers = THashMap() + var hasForwardDeclarations = false - modulesProvider.loadModuleInfos().forEach { moduleInfo -> - val metadata = modulesProvider.loadModuleMetadata(moduleInfo.name) - - for (i in metadata.fragmentNames.indices) { - val packageFqName = metadata.fragmentNames[i] - val packageFragments = metadata.fragments[i] - - for (j in packageFragments.indices) { - val packageFragment: ProtoBuf.PackageFragment = parsePackageFragment(packageFragments[j]) - - val classes: List = packageFragment.class_List - val typeAliases: List = packageFragment.`package`?.typeAliasList.orEmpty() - - if (classes.isEmpty() && typeAliases.isEmpty()) - break // this and next package fragments do not contain classifiers and can be skipped - - val packageName = CirPackageName.create(packageFqName) - val nameResolver = NameResolverImpl(packageFragment.strings, packageFragment.qualifiedNames) - - for (clazz in classes) { - if (!nameResolver.isLocalClassName(clazz.fqName)) { - val classId = CirEntityId.create(nameResolver.getQualifiedClassName(clazz.fqName)) - check(classId.packageName == packageName) - result += classId - } + modulesProvider.loadModuleInfos().forEach { moduleInfo -> + if (moduleInfo.cInteropAttributes != null) { + // this is a C-interop module + hasForwardDeclarations = true } - for (typeAlias in typeAliases) { - val typeAliasId = CirEntityId.create(packageName, CirName.create(nameResolver.getString(typeAlias.name))) - result += typeAliasId + val metadata = modulesProvider.loadModuleMetadata(moduleInfo.name) + readModule(metadata, classifiers::set) + } + + if (classifiers.isEmpty) + return CirProvidedClassifiers.EMPTY + + return CirProvidedClassifiersByModules(hasForwardDeclarations, classifiers) + } + + private val FALLBACK_FORWARD_DECLARATION_CLASS = CirProvided.Class(emptyList(), DescriptorVisibilities.PUBLIC) + } +} + +private fun readModule(metadata: SerializedMetadata, consumer: (CirEntityId, CirProvided.Classifier) -> Unit) { + for (i in metadata.fragmentNames.indices) { + val packageFqName = metadata.fragmentNames[i] + val packageFragments = metadata.fragments[i] + + val classProtosToRead = ClassProtosToRead() + + for (j in packageFragments.indices) { + val packageFragmentProto = parsePackageFragment(packageFragments[j]) + + val classProtos: List = packageFragmentProto.class_List + val typeAliasProtos: List = packageFragmentProto.`package`?.typeAliasList.orEmpty() + + if (classProtos.isEmpty() && typeAliasProtos.isEmpty()) + continue + + val packageName = CirPackageName.create(packageFqName) + val strings = NameResolverImpl(packageFragmentProto.strings, packageFragmentProto.qualifiedNames) + + classProtosToRead.addClasses(classProtos, strings) + + if (typeAliasProtos.isNotEmpty()) { + val types = TypeTable(packageFragmentProto.`package`.typeTable) + for (typeAliasProto in typeAliasProtos) { + readTypeAlias(typeAliasProto, packageName, strings, types, consumer) } } } + + classProtosToRead.forEachClassInScope(parentClassId = null) { classEntry -> + readClass(classEntry, classProtosToRead, typeParameterIndexOffset = 0, consumer) + } + } +} + +private class ClassProtosToRead { + data class ClassEntry(val classId: CirEntityId, val proto: ProtoBuf.Class) + + // key = parent class ID (or NON_EXISTING_CLASSIFIER_ID for top-level classes) + // value = class protos under this parent class (MutableList to preserve order of classes) + private val groupedByParentClassId = FactoryMap.create> { ArrayList() } + + fun addClasses(classProtos: List, strings: NameResolver) { + classProtos.forEach { classProto -> + if (strings.isLocalClassName(classProto.fqName)) return@forEach + + val classId = CirEntityId.create(strings.getQualifiedClassName(classProto.fqName)) + val parentClassId: CirEntityId = classId.getParentEntityId() ?: NON_EXISTING_CLASSIFIER_ID + + groupedByParentClassId.getValue(parentClassId) += ClassEntry(classId, classProto) + } } - return result + fun forEachClassInScope(parentClassId: CirEntityId?, block: (ClassEntry) -> Unit) { + groupedByParentClassId[parentClassId ?: NON_EXISTING_CLASSIFIER_ID]?.forEach { classEntry -> block(classEntry) } + } } + +private fun readClass( + classEntry: ClassProtosToRead.ClassEntry, + classProtosToRead: ClassProtosToRead, + typeParameterIndexOffset: Int, + consumer: (CirEntityId, CirProvided.Classifier) -> Unit +) { + val (classId, classProto) = classEntry + + val typeParameters = readTypeParameters( + typeParameterProtos = classProto.typeParameterList, + typeParameterIndexOffset = typeParameterIndexOffset + ) + val visibility = ProtoEnumFlags.descriptorVisibility(Flags.VISIBILITY.get(classProto.flags)) + val clazz = CirProvided.Class(typeParameters, visibility) + + consumer(classId, clazz) + + classProtosToRead.forEachClassInScope(parentClassId = classId) { nestedClassEntry -> + readClass(nestedClassEntry, classProtosToRead, typeParameterIndexOffset = typeParameters.size + typeParameterIndexOffset, consumer) + } +} + +private inline fun readTypeAlias( + typeAliasProto: ProtoBuf.TypeAlias, + packageName: CirPackageName, + strings: NameResolver, + types: TypeTable, + consumer: (CirEntityId, CirProvided.Classifier) -> Unit +) { + val typeAliasId = CirEntityId.create(packageName, CirName.create(strings.getString(typeAliasProto.name))) + + val typeParameterNameToIndex = HashMap() + val typeParameters = readTypeParameters( + typeParameterProtos = typeAliasProto.typeParameterList, + typeParameterIndexOffset = 0, + nameToIndexMapper = typeParameterNameToIndex::set + ) + + val underlyingType = readType(typeAliasProto.underlyingType(types), TypeReadContext(strings, types, typeParameterNameToIndex)) + val typeAlias = CirProvided.TypeAlias(typeParameters, underlyingType) + + consumer(typeAliasId, typeAlias) +} + +private inline fun readTypeParameters( + typeParameterProtos: List, + typeParameterIndexOffset: Int, + nameToIndexMapper: (name: Int, id: Int) -> Unit = { _, _ -> } +): List = + typeParameterProtos.compactMapIndexed { localIndex, typeParameterProto -> + val index = localIndex + typeParameterIndexOffset + val typeParameter = CirProvided.TypeParameter( + index = index, + variance = readVariance(typeParameterProto.variance) + ) + nameToIndexMapper(typeParameterProto.name, index) + typeParameter + } + +private class TypeReadContext( + val strings: NameResolver, + val types: TypeTable, + private val _typeParameterNameToIndex: Map +) { + val typeParameterNameToIndex: (Int) -> Int = { name -> + _typeParameterNameToIndex[name] ?: error("No type parameter index for ${strings.getString(name)}") + } + + private val _typeParameterIdToIndex = HashMap() + val typeParameterIdToIndex: (Int) -> Int = { id -> _typeParameterIdToIndex.getOrPut(id) { _typeParameterIdToIndex.size } } +} + +private fun readType(typeProto: ProtoBuf.Type, context: TypeReadContext): CirProvided.Type = + with(typeProto.abbreviatedType(context.types) ?: typeProto) { + when { + hasClassName() -> { + val classId = CirEntityId.create(context.strings.getQualifiedClassName(className)) + val outerType = typeProto.outerType(context.types)?.let { outerType -> + val outerClassType = readType(outerType, context) + check(outerClassType is CirProvided.ClassType) { "Outer type of $classId is not a class: $outerClassType" } + outerClassType + } + + CirProvided.ClassType( + classId = classId, + outerType = outerType, + arguments = readTypeArguments(argumentList, context), + isMarkedNullable = nullable + ) + } + hasTypeAliasName() -> CirProvided.TypeAliasType( + typeAliasId = CirEntityId.create(context.strings.getQualifiedClassName(typeAliasName)), + arguments = readTypeArguments(argumentList, context), + isMarkedNullable = nullable + ) + hasTypeParameter() -> CirProvided.TypeParameterType( + index = context.typeParameterIdToIndex(typeParameter), + isMarkedNullable = nullable + ) + hasTypeParameterName() -> CirProvided.TypeParameterType( + index = context.typeParameterNameToIndex(typeParameterName), + isMarkedNullable = nullable + ) + else -> error("No classifier (class, type alias or type parameter) recorded for Type") + } + } + +private fun readTypeArguments(argumentProtos: List, context: TypeReadContext): List = + argumentProtos.compactMap { argumentProto -> + val variance = readVariance(argumentProto.projection!!) ?: return@compactMap CirProvided.StarTypeProjection + val typeProto = argumentProto.type(context.types) ?: error("No type argument for non-STAR projection in Type") + + CirProvided.RegularTypeProjection( + variance = variance, + type = readType(typeProto, context) + ) + } + +@Suppress("NOTHING_TO_INLINE") +private inline fun readVariance(varianceProto: ProtoBuf.TypeParameter.Variance): Variance = + when (varianceProto) { + ProtoBuf.TypeParameter.Variance.IN -> Variance.IN_VARIANCE + ProtoBuf.TypeParameter.Variance.OUT -> Variance.OUT_VARIANCE + ProtoBuf.TypeParameter.Variance.INV -> Variance.INVARIANT + } + +@Suppress("NOTHING_TO_INLINE") +private inline fun readVariance(varianceProto: ProtoBuf.Type.Argument.Projection): Variance? = + when (varianceProto) { + ProtoBuf.Type.Argument.Projection.IN -> Variance.IN_VARIANCE + ProtoBuf.Type.Argument.Projection.OUT -> Variance.OUT_VARIANCE + ProtoBuf.Type.Argument.Projection.INV -> Variance.INVARIANT + ProtoBuf.Type.Argument.Projection.STAR -> null + } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirTreeMergerV2.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirTreeMergerV2.kt new file mode 100644 index 00000000000..4c97473094a --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/CirTreeMergerV2.kt @@ -0,0 +1,414 @@ +/* + * 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.descriptors.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 org.jetbrains.kotlin.descriptors.commonizer.CommonizerParameters +import org.jetbrains.kotlin.descriptors.commonizer.LeafCommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider.ModuleInfo +import org.jetbrains.kotlin.descriptors.commonizer.TargetProvider +import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId +import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName +import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPackageName +import org.jetbrains.kotlin.descriptors.commonizer.cir.factory.* +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ClassesToProcess.ClassEntry +import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.SerializedMetadataLibraryProvider +import org.jetbrains.kotlin.descriptors.commonizer.prettyName +import org.jetbrains.kotlin.descriptors.commonizer.utils.* +import org.jetbrains.kotlin.storage.StorageManager + +/** + * N.B. Limitations on C/Obj-C interop. + * + * [Case 1]: An interop library with two fragments for two targets. The first fragment has a forward declaration of classifier A. + * The second one has a definition of class A. Both fragments have a top-level callable (ex: function) + * with the same signature that refers to type "A" as its return type. + * + * What will happen: Forward declarations will be ignored during building CIR merged tree. So the node for class A + * will contain CirClass "A" for the second target only. This node will not succeed in commonization, and no common class + * declaration will be produced. As a result the top-level callable will not be commonized, as it refers to the type "A" + * that is not formally commonized. + * + * This is not strictly correct: The classifier "A" exists in both targets though in different form. So if the user + * would write shared source code that uses "A" and the callable, then this code would successfully compile against both targets. + * + * The reason why commonization of such classifiers is not supported yet is that this is quite a rare case that requires + * a complex implementation with potential performance penalty. + * + * [Case 2]: A library with two fragments for two targets. The first fragment is interop. The second one is not. + * Similarly to case 1, the 1st fragment has a forward declaration of a classifier, and the 2nd has a real classifier. + * + * At the moment, this is an exotic case. It could happen if someone tries to commonize an MPP library for Native and non-Native + * 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 CirTreeMergerV2( + private val storageManager: StorageManager, + private val classifiers: CirKnownClassifiers, + private val parameters: CommonizerParameters +) { + class CirTreeMergeResult( + val root: CirRootNode, + val missingModuleInfos: Map> + ) + + 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 + } + + fun create(typeParameters: List): CirTreeMergingContext { + val newTypeResolver = typeResolver.create(typeParameters) + return if (newTypeResolver !== typeResolver) + CirTreeMergingContext(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) + + // remember any exported forward declarations from common fragments of dependee modules + parameters.dependencyModulesProvider?.loadModuleInfos()?.forEach(::processCInteropModuleAttributes) + + 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] = CirRootFactory.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) + ) + ) + ) + + commonModuleInfos.forEach { moduleInfo -> + val metadata = targetProvider.modulesProvider.loadModuleMetadata(moduleInfo.name) + val module = KlibModuleMetadata.read(SerializedMetadataLibraryProvider(metadata)) + processModule(context, rootNode, moduleInfo, module) + } + } + + private fun processModule( + context: CirTreeMergingContext, + rootNode: CirRootNode, + moduleInfo: ModuleInfo, + module: KlibModuleMetadata + ) { + processCInteropModuleAttributes(moduleInfo) + + val moduleName: CirName = CirName.create(module.name) + val moduleNode: CirModuleNode = rootNode.modules.getOrPut(moduleName) { + buildModuleNode(storageManager, leafTargetsSize) + } + moduleNode.targetDeclarations[context.targetIndex] = CirModuleFactory.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] = CirPackageFactory.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] = CirPropertyFactory.create( + 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] = CirFunctionFactory.create( + 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) + + CirClassFactory.create(className, clazz, context.typeResolver) + } + is ClassEntry.EnumEntry -> { + clazz = null + isEnumEntry = true + + CirClassFactory.createDefaultEnumEntry( + 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] = CirClassConstructorFactory.create( + 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] = CirTypeAliasFactory.create( + name = typeAliasName, + source = typeAlias, + typeResolver = context.typeResolver + ) + } + + private fun processCInteropModuleAttributes(moduleInfo: ModuleInfo) { + val cInteropAttributes = moduleInfo.cInteropAttributes ?: return + val exportForwardDeclarations = cInteropAttributes.exportForwardDeclarations.takeIf { it.isNotEmpty() } ?: return + + exportForwardDeclarations.forEach { classFqName -> + // Class has synthetic package FQ name (cnames/objcnames). Need to transfer it to the main package. + val packageName = CirPackageName.create(classFqName.substringBeforeLast('.', missingDelimiterValue = "")) + val className = CirName.create(classFqName.substringAfterLast('.')) + + classifiers.forwardDeclarations.addExportedForwardDeclaration(CirEntityId.create(packageName, className)) + } + } +} + +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) } + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/approximationKeys.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/approximationKeys.kt index 93249d1a8ac..ed1bbb82670 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/approximationKeys.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/approximationKeys.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.descriptors.commonizer.mergedtree +import kotlinx.metadata.* +import kotlinx.metadata.klib.annotations import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName import org.jetbrains.kotlin.descriptors.commonizer.cir.CirTypeSignature @@ -21,6 +23,11 @@ data class PropertyApproximationKey( CirName.create(property.name), property.extensionReceiverParameter?.type?.signature ) + + constructor(property: KmProperty, typeParameterResolver: TypeParameterResolver) : this( + CirName.create(property.name), + property.receiverParameterType?.computeSignature(typeParameterResolver) + ) } /** Used for approximation of [SimpleFunctionDescriptor]s before running concrete [Commonizer]s */ @@ -37,6 +44,13 @@ data class FunctionApproximationKey( function.extensionReceiverParameter?.type?.signature ) + constructor(function: KmFunction, typeParameterResolver: TypeParameterResolver) : this( + CirName.create(function.name), + function.valueParameters.computeSignatures(typeParameterResolver), + additionalValueParameterNamesHash(function.annotations, function.valueParameters), + function.receiverParameterType?.computeSignature(typeParameterResolver) + ) + override fun equals(other: Any?): Boolean { if (other !is FunctionApproximationKey) return false @@ -63,6 +77,11 @@ data class ConstructorApproximationKey( additionalValueParameterNamesHash(constructor) ) + constructor(constructor: KmConstructor, typeParameterResolver: TypeParameterResolver) : this( + constructor.valueParameters.computeSignatures(typeParameterResolver), + additionalValueParameterNamesHash(constructor.annotations, constructor.valueParameters) + ) + override fun equals(other: Any?): Boolean { if (other !is ConstructorApproximationKey) return false @@ -75,9 +94,24 @@ data class ConstructorApproximationKey( .appendHashCode(additionalValueParametersNamesHash) } +interface TypeParameterResolver { + fun resolveTypeParameter(id: Int): KmTypeParameter? + + companion object { + val EMPTY = object : TypeParameterResolver { + override fun resolveTypeParameter(id: Int): KmTypeParameter? = null + } + } +} + +@JvmName("toTypeSignaturesDescriptors") @Suppress("NOTHING_TO_INLINE") private inline fun List.toTypeSignatures(): Array = - Array(size) { index -> this[index].type.signature } + if (isEmpty()) emptyArray() else Array(size) { index -> this[index].type.signature } + +@Suppress("NOTHING_TO_INLINE") +private inline fun List.computeSignatures(typeParameterResolver: TypeParameterResolver): Array = + if (isEmpty()) emptyArray() else Array(size) { index -> this[index].type?.computeSignature(typeParameterResolver).orEmpty() } private fun additionalValueParameterNamesHash(callable: FunctionDescriptor): Int { // TODO: add more precise checks when more languages than C & ObjC are supported @@ -86,3 +120,11 @@ private fun additionalValueParameterNamesHash(callable: FunctionDescriptor): Int return callable.valueParameters.fold(0) { acc, next -> acc.appendHashCode(next.name.asString()) } } + +private fun additionalValueParameterNamesHash(annotations: List, valueParameters: List): Int { + // TODO: add more precise checks when more languages than C & ObjC are supported + if (annotations.none { it.isObjCInteropCallableAnnotation }) + return 0 // do not calculate hash for non-ObjC callables + + return valueParameters.fold(0) { acc, next -> acc.appendHashCode(next.name) } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/entityBuilders.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/entityBuilders.kt index bd10dbe674d..6bfe265a6dd 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/entityBuilders.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/entityBuilders.kt @@ -133,6 +133,7 @@ internal fun CirClassConstructor.buildClassConstructor( flags = classConstructorFlags() ).also { constructor -> annotations.mapTo(constructor.annotations) { it.buildAnnotation() } + // TODO: nowhere to write constructor type parameters valueParameters.mapTo(constructor.valueParameters) { it.buildValueParameter(context) } } @@ -156,11 +157,11 @@ internal fun CirProperty.buildProperty( getterFlags = getter?.propertyAccessorFlags(this, this) ?: NO_FLAGS, setterFlags = setter?.let { setter -> setter.propertyAccessorFlags(setter, this) } ?: NO_FLAGS ).also { property -> - // TODO unclear where to write backing/delegate field annotations, see KT-44625 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 CirConstantValue.NullValue }?.buildAnnotationArgument() + // TODO unclear where to write backing/delegate field annotations, see KT-44625 + property.compileTimeValue = compileTimeInitializer.takeIf { it !is CirConstantValue.NullValue }?.buildAnnotationArgument() typeParameters.buildTypeParameters(context, output = property.typeParameters) extensionReceiver?.let { receiver -> // TODO nowhere to write receiver annotations, see KT-42490 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 index ae0c53f3ca2..02de60b17c1 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/flags.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/metadata/flags.kt @@ -8,13 +8,11 @@ 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.* import org.jetbrains.kotlin.descriptors.commonizer.cir.* internal const val NO_FLAGS: Flags = 0 +internal val ALWAYS_HAS_ANNOTATIONS: Flags = flagsOf(Flag.Common.HAS_ANNOTATIONS) internal fun CirFunction.functionFlags(isExpect: Boolean): Flags = flagsOfNotNull( @@ -100,6 +98,48 @@ internal fun CirTypeAlias.typeAliasFlags(): Flags = visibilityFlag ) +@Suppress("NOTHING_TO_INLINE") +internal inline fun decodeVisibility(flags: Flags): DescriptorVisibility = + when { + Flag.Common.IS_PUBLIC(flags) -> DescriptorVisibilities.PUBLIC + Flag.Common.IS_PROTECTED(flags) -> DescriptorVisibilities.PROTECTED + Flag.Common.IS_INTERNAL(flags) -> DescriptorVisibilities.INTERNAL + Flag.Common.IS_PRIVATE(flags) -> DescriptorVisibilities.PRIVATE + else -> error("Can't decode visibility from flags: $flags") + } + +@Suppress("NOTHING_TO_INLINE") +internal inline fun decodeModality(flags: Flags): Modality = + when { + Flag.Common.IS_FINAL(flags) -> Modality.FINAL + Flag.Common.IS_ABSTRACT(flags) -> Modality.ABSTRACT + Flag.Common.IS_OPEN(flags) -> Modality.OPEN + Flag.Common.IS_SEALED(flags) -> Modality.SEALED + else -> error("Can't decode modality from flags: $flags") + } + +@Suppress("NOTHING_TO_INLINE") +internal inline fun decodeCallableKind(flags: Flags): CallableMemberDescriptor.Kind = + when { + Flag.Property.IS_DECLARATION(flags) /*|| Flag.Function.IS_DECLARATION(flags)*/ -> CallableMemberDescriptor.Kind.DECLARATION + Flag.Property.IS_FAKE_OVERRIDE(flags) /*|| Flag.Function.IS_FAKE_OVERRIDE(flags)*/ -> CallableMemberDescriptor.Kind.FAKE_OVERRIDE + Flag.Property.IS_DELEGATION(flags) /*|| Flag.Function.IS_DELEGATION(flags)*/ -> CallableMemberDescriptor.Kind.DELEGATION + Flag.Property.IS_SYNTHESIZED(flags) /*|| Flag.Function.IS_SYNTHESIZED(flags)*/ -> CallableMemberDescriptor.Kind.SYNTHESIZED + else -> error("Can't decode callable kind from flags: $flags") + } + +@Suppress("NOTHING_TO_INLINE") +internal inline fun decodeClassKind(flags: Flags): ClassKind = + when { + Flag.Class.IS_CLASS(flags) -> ClassKind.CLASS + Flag.Class.IS_INTERFACE(flags) -> ClassKind.INTERFACE + Flag.Class.IS_ENUM_CLASS(flags) -> ClassKind.ENUM_CLASS + Flag.Class.IS_ENUM_ENTRY(flags) -> ClassKind.ENUM_ENTRY + Flag.Class.IS_ANNOTATION_CLASS(flags) -> ClassKind.ANNOTATION_CLASS + Flag.Class.IS_OBJECT(flags) || Flag.Class.IS_COMPANION_OBJECT(flags) -> ClassKind.OBJECT + else -> error("Can't decode class kind from flags: $flags") + } + private inline val CirHasAnnotations.hasAnnotationsFlag: Flag? get() = if (annotations.isNotEmpty()) Flag.Common.HAS_ANNOTATIONS else null diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/excludes.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/excludes.kt index 47ab197c7c9..7e5ca670e52 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/excludes.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/excludes.kt @@ -5,6 +5,10 @@ package org.jetbrains.kotlin.descriptors.commonizer.utils +import kotlinx.metadata.Flag +import kotlinx.metadata.KmFunction +import kotlinx.metadata.KmProperty +import kotlinx.metadata.klib.annotations import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor @@ -13,5 +17,21 @@ internal const val KNI_BRIDGE_FUNCTION_PREFIX = "kniBridge" internal fun SimpleFunctionDescriptor.isKniBridgeFunction() = name.asString().startsWith(KNI_BRIDGE_FUNCTION_PREFIX) +@Suppress("NOTHING_TO_INLINE") +internal inline fun KmFunction.isKniBridgeFunction() = + name.startsWith(KNI_BRIDGE_FUNCTION_PREFIX) + internal fun SimpleFunctionDescriptor.isDeprecatedTopLevelFunction() = containingDeclaration is PackageFragmentDescriptor && annotations.hasAnnotation(DEPRECATED_ANNOTATION_FQN) + +@Suppress("NOTHING_TO_INLINE") +internal inline fun KmFunction.isTopLevelDeprecatedFunction(isTopLevel: Boolean) = + isTopLevel && annotations.any { it.className == DEPRECATED_ANNOTATION_FULL_NAME } + +@Suppress("NOTHING_TO_INLINE") +internal inline fun KmProperty.isFakeOverride() = + Flag.Property.IS_FAKE_OVERRIDE(flags) + +@Suppress("NOTHING_TO_INLINE") +internal inline fun KmFunction.isFakeOverride() = + Flag.Function.IS_FAKE_OVERRIDE(flags) diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/misc.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/misc.kt index 7716e7af808..f5c86e7bcbd 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/misc.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/misc.kt @@ -95,6 +95,15 @@ internal inline fun compactMapOf(key1: K, value1: V, key2: K, value internal inline fun Iterable.firstNonNull() = firstIsInstance() +internal inline fun Collection.foldToMap(keySelector: (T) -> K): Map> { + val result = fold(THashMap>()) { accumulator, element -> + accumulator.getOrPut(keySelector(element)) { ArrayList() } += element + accumulator + } + + return result.compactMapValues { (_, elements) -> elements.compact() } +} + internal fun Any?.isNull(): Boolean = this == null @Suppress("NOTHING_TO_INLINE") diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/names.kt similarity index 78% rename from native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt rename to native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/names.kt index f36c8e1af22..e1fb21956f7 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/names.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.descriptors.commonizer.utils import kotlinx.metadata.ClassName +import kotlinx.metadata.KmAnnotation import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -17,16 +18,20 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.serialization.konan.impl.ForwardDeclarationsFqNames internal val DEPRECATED_ANNOTATION_FQN: FqName = FqName(Deprecated::class.java.name) -internal val DEPRECATED_ANNOTATION_CLASS_ID: CirEntityId = CirEntityId.create("kotlin/Deprecated") +internal const val DEPRECATED_ANNOTATION_FULL_NAME: ClassName = "kotlin/Deprecated" +internal val DEPRECATED_ANNOTATION_CLASS_ID: CirEntityId = CirEntityId.create(DEPRECATED_ANNOTATION_FULL_NAME) -internal val ANY_CLASS_ID: CirEntityId = CirEntityId.create("kotlin/Any") -private val NOTHING_CLASS_ID: CirEntityId = CirEntityId.create("kotlin/Nothing") +internal const val ANY_CLASS_FULL_NAME: ClassName = "kotlin/Any" +internal val ANY_CLASS_ID: CirEntityId = CirEntityId.create(ANY_CLASS_FULL_NAME) internal val SPECIAL_CLASS_WITHOUT_SUPERTYPES_CLASS_IDS: List = listOf( ANY_CLASS_ID, - NOTHING_CLASS_ID + CirEntityId.create("kotlin/Nothing") ) +// illegal Kotlin classifier name, for special purposes only +internal val NON_EXISTING_CLASSIFIER_ID = CirEntityId.create("$0") + internal val SPECIAL_CLASS_WITHOUT_SUPERTYPES_CLASS_NAMES: List = SPECIAL_CLASS_WITHOUT_SUPERTYPES_CLASS_IDS.map(CirEntityId::toString) @@ -49,6 +54,10 @@ private val OBJC_INTEROP_CALLABLE_ANNOTATIONS: List = listOf( CirName.create("ObjCFactory") ) +private val OBJC_INTEROP_CALLABLE_ANNOTATION_FULL_NAMES: List = OBJC_INTEROP_CALLABLE_ANNOTATIONS.map { name -> + CirEntityId.create(CINTEROP_PACKAGE, name).toString() +} + internal val DEFAULT_CONSTRUCTOR_NAME: CirName = CirName.create("") internal val DEFAULT_SETTER_VALUE_NAME: CirName = CirName.create("value") @@ -70,3 +79,6 @@ internal val AnnotationDescriptor.isObjCInteropCallableAnnotation: Boolean return CirName.create(classifier.name) in OBJC_INTEROP_CALLABLE_ANNOTATIONS && (classifier.containingDeclaration as? PackageFragmentDescriptor)?.fqName?.let(CirPackageName::create) == CINTEROP_PACKAGE } + +internal val KmAnnotation.isObjCInteropCallableAnnotation: Boolean + get() = className in OBJC_INTEROP_CALLABLE_ANNOTATION_FULL_NAMES diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/type.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/type.kt index f63fb0847eb..0d2116f8da8 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/type.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/type.kt @@ -5,12 +5,15 @@ package org.jetbrains.kotlin.descriptors.commonizer.utils +import gnu.trove.TIntHashSet +import kotlinx.metadata.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPackageName import org.jetbrains.kotlin.descriptors.commonizer.cir.CirTypeSignature +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.TypeParameterResolver import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isNullableAny @@ -44,9 +47,21 @@ internal val ClassifierDescriptorWithTypeParameters.classifierId: CirEntityId internal inline val TypeParameterDescriptor.filteredUpperBounds: List get() = upperBounds.takeUnless { it.singleOrNull()?.isNullableAny() == true } ?: emptyList() +internal inline val KmTypeParameter.filteredUpperBounds: List + get() = upperBounds.takeUnless { it.singleOrNull()?.isNullableAny == true } ?: emptyList() + internal inline val ClassDescriptor.filteredSupertypes: Collection get() = typeConstructor.supertypes.takeUnless { it.size == 1 && KotlinBuiltIns.isAny(it.first()) } ?: emptyList() +internal inline val KmClass.filteredSupertypes: List + get() = supertypes.takeUnless { it.singleOrNull()?.isAny == true } ?: emptyList() + +private inline val KmType.isNullableAny: Boolean + get() = (classifier as? KmClassifier.Class)?.name == ANY_CLASS_FULL_NAME && Flag.Type.IS_NULLABLE(flags) + +private inline val KmType.isAny: Boolean + get() = (classifier as? KmClassifier.Class)?.name == ANY_CLASS_FULL_NAME && !Flag.Type.IS_NULLABLE(flags) + internal val KotlinType.signature: CirTypeSignature get() { // use of interner saves up to 95% of duplicates @@ -100,5 +115,84 @@ private fun StringBuilder.buildTypeSignature(type: KotlinType, exploredTypeParam } } +internal fun KmType.computeSignature(typeParameterResolver: TypeParameterResolver): CirTypeSignature { + // use of interner saves up to 95% of duplicates + return typeSignatureInterner.intern( + buildString { + buildTypeSignature( + type = this@computeSignature, + typeParameterResolver = typeParameterResolver, + exploredTypeParameters = TIntHashSet() + ) + } + ) +} + +private fun StringBuilder.buildTypeSignature( + type: KmType, + typeParameterResolver: TypeParameterResolver, + exploredTypeParameters: TIntHashSet +) { + when (val classifier = type.classifier) { + is KmClassifier.TypeParameter -> { + // N.B this is type parameter type + val typeParameter = typeParameterResolver.resolveTypeParameter(classifier.id) + ?: error("Unresolved type parameter #${classifier.id} in type ${type.classifier}") + + append(typeParameter.name) + + if (exploredTypeParameters.add(classifier.id)) { // print upper bounds once the first time when type parameter type is met + append(':').append('[') + typeParameter.filteredUpperBounds.forEachIndexed { index, upperBoundType -> + if (index > 0) + append(',') + buildTypeSignature(upperBoundType, typeParameterResolver, exploredTypeParameters) + } + append(']') + } + + if (Flag.Type.IS_NULLABLE(type.flags)) + append('?') + } + else -> { + val abbreviation = type.abbreviatedType ?: type + + val classifierId = when (val abbreviationClassifier = abbreviation.classifier) { + is KmClassifier.Class -> abbreviationClassifier.name + is KmClassifier.TypeAlias -> abbreviationClassifier.name + else -> error("Unexpected classifier type for non-type-parameter type: ${type.classifier}") + } + append(classifierId) + + val arguments = abbreviation.arguments + if (arguments.isNotEmpty()) { + append('<') + arguments.forEachIndexed { index, argument -> + if (index > 0) + append(',') + + val variance = argument.variance + val argumentType = argument.type + + if (variance == null || argumentType == null) + append('*') + else { + when (variance) { + KmVariance.INVARIANT -> Unit + KmVariance.IN -> append("in ") + KmVariance.OUT -> append("out ") + } + buildTypeSignature(argumentType, typeParameterResolver, exploredTypeParameters) + } + } + append('>') + } + + if (Flag.Type.IS_NULLABLE(abbreviation.flags)) + append('?') + } + } +} + // dedicated to hold unique entries of "signature" private val typeSignatureInterner = Interner() diff --git a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt index c20bf9b7dee..fcfa4e7bb29 100644 --- a/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt +++ b/native/commonizer/testData/classifierCommonization/typeAliases/commonized/common/package_root.kt @@ -43,8 +43,7 @@ typealias Y = V // TA at the RHS with the different nullability of own RHS // Supertypes: expect class FILE : kotlinx.cinterop.CStructVar -typealias uuid_t = kotlinx.cinterop.CPointer> -// ^^^ TODO: ideally, it should be CArrayPointer +typealias uuid_t = kotlinx.cinterop.CArrayPointer typealias __darwin_uuid_t = kotlinx.cinterop.CArrayPointer expect val uuid: uuid_t diff --git a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizerTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizerTest.kt index a38dd76335c..b12f0591815 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizerTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizerTest.kt @@ -34,7 +34,8 @@ class TypeCommonizerTest : AbstractCommonizerTest() { commonizedNodes = CirCommonizedClassifierNodes.default(), forwardDeclarations = CirForwardDeclarations.default(), commonDependencies = object : CirProvidedClassifiers { - override fun hasClassifier(classifierId: CirEntityId): Boolean = classifierId.packageName.isUnderStandardKotlinPackages + override fun hasClassifier(classifierId: CirEntityId) = classifierId.packageName.isUnderStandardKotlinPackages + override fun classifier(classifierId: CirEntityId) = error("This method should not be called") } ) } diff --git a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/assertions.kt b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/assertions.kt index b237af58818..b31808e4a52 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/assertions.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/assertions.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.descriptors.commonizer.utils import kotlinx.metadata.klib.KlibModuleMetadata +import kotlinx.metadata.klib.annotations import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget import org.jetbrains.kotlin.descriptors.commonizer.identityString import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.MetadataDeclarationsComparator @@ -52,30 +53,46 @@ fun assertModulesAreEqual(reference: SerializedMetadata, generated: SerializedMe private val FILTER_OUT_ACCEPTABLE_MISMATCHES: (Mismatch) -> Boolean = { mismatch -> var isAcceptableMismatch = false // don't filter it out by default - if (mismatch is Mismatch.MissingEntity) { - if (mismatch.kind == EntityKind.TypeKind.ABBREVIATED) { - val usefulPath = mismatch.path - .dropWhile { it !is PathElement.Package } - .drop(1) + when (mismatch) { + is Mismatch.MissingEntity -> when (mismatch.kind) { + EntityKind.TypeKind.ABBREVIATED -> { + val usefulPath = mismatch.path + .dropWhile { it !is PathElement.Package } + .drop(1) - if (mismatch.missingInA) { - if (usefulPath.size == 2 - && usefulPath[0] is PathElement.TypeAlias - && (usefulPath[1] as? PathElement.Type)?.kind == EntityKind.TypeKind.EXPANDED - ) { - // extra abbreviated type appeared in commonized declaration, it's OK - isAcceptableMismatch = true + if (mismatch.missingInA) { + if (usefulPath.size == 2 + && usefulPath[0] is PathElement.TypeAlias + && (usefulPath[1] as? PathElement.Type)?.kind == EntityKind.TypeKind.EXPANDED + ) { + // extra abbreviated type appeared in commonized declaration, it's OK + isAcceptableMismatch = true + } + } else /*if (mismatch.missingInB)*/ { + if (usefulPath.size > 2 + && usefulPath.any { (it as? PathElement.Type)?.kind == EntityKind.TypeKind.RETURN } + && usefulPath[usefulPath.size - 2] is PathElement.TypeArgument + && (usefulPath[usefulPath.size - 1] as? PathElement.Type)?.kind == EntityKind.TypeKind.TYPE_ARGUMENT + ) { + // extra abbreviated type gone in type argument of commonized declaration, it's OK + isAcceptableMismatch = true + } } - } else /*if (mismatch.missingInB)*/ { - if (usefulPath.size > 2 - && usefulPath.any { (it as? PathElement.Type)?.kind == EntityKind.TypeKind.RETURN } - && usefulPath[usefulPath.size - 2] is PathElement.TypeArgument - && (usefulPath[usefulPath.size - 1] as? PathElement.Type)?.kind == EntityKind.TypeKind.TYPE_ARGUMENT + } + else -> Unit + } + is Mismatch.DifferentValues -> when (mismatch.kind) { + EntityKind.FlagKind.REGULAR, EntityKind.FlagKind.GETTER, EntityKind.FlagKind.SETTER -> { + if (mismatch.name == "HAS_ANNOTATIONS" + && mismatch.valueA == true + && mismatch.valueB == false + && (mismatch.path.last() as? PathElement.Property)?.propertyA?.annotations.isNullOrEmpty() ) { - // extra abbreviated type gone in type argument of commonized declaration, it's OK + // backing or delegate field annotations were not serialized (KT-44625) but the corresponding flag was raised, it's OK isAcceptableMismatch = true } } + else -> Unit } }