diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedAnnotationDescriptor.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedAnnotationDescriptor.kt index 7a009a4ba67..536e4f0f7c6 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedAnnotationDescriptor.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedAnnotationDescriptor.kt @@ -10,22 +10,16 @@ import org.jetbrains.kotlin.descriptors.ClassKind.ANNOTATION_CLASS import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ir.CirAnnotation +import org.jetbrains.kotlin.descriptors.commonizer.utils.concat import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.constants.AnnotationValue -import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.storage.getValue class CommonizedAnnotationDescriptor( - private val targetComponents: TargetDeclarationsBuilderComponents, - override val fqName: FqName, - rawValueArguments: Map> + targetComponents: TargetDeclarationsBuilderComponents, + cirAnnotation: CirAnnotation ) : AnnotationDescriptor { - constructor(targetComponents: TargetDeclarationsBuilderComponents, cirAnnotation: CirAnnotation) : this( - targetComponents, - cirAnnotation.fqName, - cirAnnotation.allValueArguments - ) + override val fqName: FqName = cirAnnotation.fqName override val type by targetComponents.storageManager.createLazyValue { val annotationClass = findClassOrTypeAlias(targetComponents, fqName) @@ -36,17 +30,10 @@ class CommonizedAnnotationDescriptor( } override val allValueArguments by targetComponents.storageManager.createLazyValue { - rawValueArguments.mapValues { (_, value) -> substituteValueArgument(value) } + cirAnnotation.constantValueArguments concat cirAnnotation.annotationValueArguments.mapValues { (_, nestedCirAnnotation) -> + AnnotationValue(CommonizedAnnotationDescriptor(targetComponents, nestedCirAnnotation)) + } } override val source: SourceElement get() = SourceElement.NO_SOURCE - - private fun substituteValueArgument(value: ConstantValue<*>) = - (value as? AnnotationValue)?.value?.let { nestedAnnotationDescriptor -> - // re-build annotation descriptors - val fqName = nestedAnnotationDescriptor.fqName - ?: error("Annotation with no FQ name: ${nestedAnnotationDescriptor::class.java}, $nestedAnnotationDescriptor") - - AnnotationValue(CommonizedAnnotationDescriptor(targetComponents, fqName, nestedAnnotationDescriptor.allValueArguments)) - } ?: value // keep other values as they are platform agnostic } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/builderUtils.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/builderUtils.kt index 37b43c2e43c..42373788625 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/builderUtils.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/builderUtils.kt @@ -120,11 +120,8 @@ internal fun CirSimpleType.buildType( } } - // TODO: commonize annotations, KT-34234 - val typeAnnotations = if (!targetComponents.isCommon) annotations.buildDescriptors(targetComponents) else Annotations.EMPTY - val simpleType = simpleType( - annotations = typeAnnotations, + annotations = annotations.buildDescriptors(targetComponents), constructor = classifier.typeConstructor, arguments = arguments.map { it.buildArgument(targetComponents, typeParameterResolver) }, nullable = isMarkedNullable, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt index a79fa2bd27d..fcbfdc37cd7 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractFunctionOrPropertyCommonizer.kt @@ -13,7 +13,9 @@ import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ir.CirFunctionOrPr import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ir.isNonAbstractMemberInInterface import org.jetbrains.kotlin.name.Name -abstract class AbstractFunctionOrPropertyCommonizer(cache: CirClassifiersCache) : AbstractStandardCommonizer() { +abstract class AbstractFunctionOrPropertyCommonizer( + cache: CirClassifiersCache +) : AbstractStandardCommonizer() { protected lateinit var name: Name protected val modality = ModalityCommonizer.default() protected val visibility = VisibilityCommonizer.lowering() diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractNullableCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractNullableCommonizer.kt index f883db32aa9..a7bbcae3850 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractNullableCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractNullableCommonizer.kt @@ -34,7 +34,7 @@ abstract class AbstractNullableCommonizer( final override fun commonizeWith(next: T?): Boolean { state = when (state) { - State.ERROR -> State.ERROR + State.ERROR -> return false State.EMPTY -> next?.let { wrapped = wrappedCommonizerFactory() doCommonizeWith(next) diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AnnotationsCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AnnotationsCommonizer.kt new file mode 100644 index 00000000000..e460991d02a --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/AnnotationsCommonizer.kt @@ -0,0 +1,209 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.descriptors.commonizer.core + +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ir.CirAnnotation +import org.jetbrains.kotlin.descriptors.commonizer.utils.DEPRECATED_ANNOTATION_FQN +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.constants.ArrayValue +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.resolve.constants.EnumValue +import org.jetbrains.kotlin.resolve.constants.StringValue + +/** + * This is limited implementation of annotations commonizer. It helps to commonize only [kotlin.Deprecated] annotations. + */ +class AnnotationsCommonizer : AbstractStandardCommonizer, List>() { + private var deprecatedAnnotationCommonizer: DeprecatedAnnotationCommonizer? = null + + override fun commonizationResult(): List { + val deprecatedAnnotation = deprecatedAnnotationCommonizer?.result ?: return emptyList() + return listOf(deprecatedAnnotation) + } + + override fun initialize(first: List) = Unit + + override fun doCommonizeWith(next: List): Boolean { + val nextDeprecatedAnnotation = next.firstOrNull { it.fqName == DEPRECATED_ANNOTATION_FQN } ?: return true + val deprecatedAnnotationCommonizer = deprecatedAnnotationCommonizer ?: DeprecatedAnnotationCommonizer() + return deprecatedAnnotationCommonizer.commonizeWith(nextDeprecatedAnnotation) + } +} + +private class DeprecatedAnnotationCommonizer : Commonizer { + private var level: DeprecationLevel? = null // null level means that state is empty + private var message: String? = null // null -> message is not equal + private var replaceWithExpression: String? = null // null -> replaceWith is not equal + private var replaceWithImports: List? = null + + override val result: CirAnnotation + get() { + if (level == null) throw IllegalCommonizerStateException() + + return CirAnnotation.create( + fqName = DEPRECATED_ANNOTATION_FQN, + constantValueArguments = mapOf>( + PROPERTY_NAME_LEVEL to level!!.toDeprecationLevelValue(), + PROPERTY_NAME_MESSAGE to message.toDeprecationMessageValue() + ), + annotationValueArguments = mapOf( + PROPERTY_NAME_REPLACE_WITH to replaceWithExpression.toReplaceWithValue(replaceWithImports) + ) + ) + } + + override fun commonizeWith(next: CirAnnotation): Boolean { + val nextLevel: DeprecationLevel = next.getDeprecationLevel() ?: DeprecationLevel.HIDDEN + val nextMessage: String? = next.getDeprecationMessage() + val nextReplaceWith: CirAnnotation? = next.getReplaceWith() + val nextReplaceWithExpression: String? = nextReplaceWith?.getReplaceWithExpression() + val nextReplaceWithImports: List? = nextReplaceWith?.getReplaceWithImports() + + return if (level != null) { + doCommonizeWith(nextLevel, nextMessage, nextReplaceWithExpression, nextReplaceWithImports) + } else { + // empty, just fill in + initialize(nextLevel, nextMessage, nextReplaceWithExpression, nextReplaceWithImports) + true + } + } + + private fun initialize( + nextLevel: DeprecationLevel, + nextMessage: String?, + nextReplaceWithExpression: String?, + nextReplaceWithImports: List? + ) { + level = nextLevel + message = nextMessage + + if (nextReplaceWithExpression != null && nextReplaceWithImports != null) { + replaceWithExpression = nextReplaceWithExpression + replaceWithImports = nextReplaceWithImports + } + } + + private fun doCommonizeWith( + nextLevel: DeprecationLevel, + nextMessage: String?, + nextReplaceWithExpression: String?, + nextReplaceWithImports: List? + ): Boolean { + if (nextLevel.ordinal > level!!.ordinal) + level = nextLevel + + if (nextMessage != message) + message = null + + if (nextReplaceWithExpression != replaceWithExpression || nextReplaceWithImports != replaceWithImports) { + replaceWithExpression = null + replaceWithImports = null + } + + return true + } + + @Suppress("NOTHING_TO_INLINE") + companion object { + private val PROPERTY_NAME_MESSAGE = Name.identifier(Deprecated::message.name) + private val PROPERTY_NAME_REPLACE_WITH = Name.identifier(Deprecated::replaceWith.name) + private val PROPERTY_NAME_LEVEL = Name.identifier(Deprecated::level.name) + + private val PROPERTY_NAME_EXPRESSION = Name.identifier(ReplaceWith::expression.name) + private val PROPERTY_NAME_IMPORTS = Name.identifier(ReplaceWith::imports.name) + + // Optimization: Keep most frequently used message constants. + private val FREQUENTLY_USED_MESSAGE_VALUES: Map = listOf( + "Use constructor instead", + "Use factory method instead" + ).associateWith { StringValue(it) } + + private val FALLBACK_MESSAGE_VALUE = StringValue("See actual declarations for concrete deprecation messages") + + private val DEPRECATION_LEVEL_FQN = FqName(DeprecationLevel::class.java.name) + private val DEPRECATION_LEVEL_CLASS_ID = ClassId.topLevel(DEPRECATION_LEVEL_FQN) + + // Optimization: Keep DeprecationLevel enum constants. + private val DEPRECATION_LEVEL_ENUM_ENTRY_VALUES: Map = DeprecationLevel.values().associate { + it.name to EnumValue(DEPRECATION_LEVEL_CLASS_ID, Name.identifier(it.name)) + } + + private val REPLACE_WITH_FQN = FqName(ReplaceWith::class.java.name) + + // Optimization: Keep empty ReplaceWith instance of CirAnnotation. + private val EMPTY_REPLACE_WITH_CIR_ANNOTATION = createReplaceWithAnnotation("", emptyList()) + + private fun CirAnnotation.getDeprecationMessage(): String? = constantValueArguments.getString(PROPERTY_NAME_MESSAGE) + + private fun String?.toDeprecationMessageValue(): StringValue = + if (this == null) + FALLBACK_MESSAGE_VALUE + else + FREQUENTLY_USED_MESSAGE_VALUES[this] ?: StringValue(this) + + private fun CirAnnotation.getDeprecationLevel(): DeprecationLevel? { + val enumEntryName = constantValueArguments.getEnumEntryName(PROPERTY_NAME_LEVEL) ?: return null + return DeprecationLevel.values().firstOrNull { it.name == enumEntryName } + } + + private fun DeprecationLevel.toDeprecationLevelValue(): EnumValue = + DEPRECATION_LEVEL_ENUM_ENTRY_VALUES.getValue(name) + + private fun CirAnnotation.getReplaceWith(): CirAnnotation? = + annotationValueArguments.getAnnotation(PROPERTY_NAME_REPLACE_WITH) + + private fun CirAnnotation.getReplaceWithExpression(): String? = + constantValueArguments.getString(PROPERTY_NAME_EXPRESSION) + + private fun CirAnnotation.getReplaceWithImports(): List? = + constantValueArguments.getStringArray(PROPERTY_NAME_IMPORTS) + + private fun String?.toReplaceWithValue(imports: List?): CirAnnotation = + if (this == null || imports == null) + EMPTY_REPLACE_WITH_CIR_ANNOTATION + else + createReplaceWithAnnotation(this, imports) + + private inline fun Map>.getString(name: Name): String? = + (this[name] as? StringValue)?.value + + private inline fun Map>.getEnumEntryName(name: Name): String? = + (this[name] as? EnumValue)?.enumEntryName?.asString() + + private inline fun Map.getAnnotation(name: Name): CirAnnotation? = + this[name] + + private inline fun Map>.getStringArray(name: Name): List? { + val elements: List> = (this[name] as? ArrayValue)?.value ?: return null + if (elements.isEmpty()) return emptyList() + + val result = mutableListOf() + for (element in elements) { + if (element is StringValue) { + result += element.value + } else + return null + } + + return result + } + + private inline fun createReplaceWithAnnotation(expression: String, imports: List): CirAnnotation = + CirAnnotation.create( + fqName = REPLACE_WITH_FQN, + constantValueArguments = mapOf( + PROPERTY_NAME_EXPRESSION to StringValue(expression), + PROPERTY_NAME_IMPORTS to ArrayValue( + value = imports.map { StringValue(it) }, + computeType = { it.builtIns.getArrayElementType(it.builtIns.stringType) } + ) + ), + annotationValueArguments = emptyMap() + ) + } +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/Commonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/Commonizer.kt index fa1b38bd587..cebc6b038f3 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/Commonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/Commonizer.kt @@ -26,13 +26,15 @@ abstract class AbstractStandardCommonizer : Commonizer { } final override fun commonizeWith(next: T): Boolean { - if (state == State.ERROR) - return false + val result = when (state) { + State.ERROR -> return false + State.EMPTY -> { + initialize(next) + doCommonizeWith(next) + } + State.IN_PROGRESS -> doCommonizeWith(next) + } - if (state == State.EMPTY) - initialize(next) - - val result = doCommonizeWith(next) state = if (!result) State.ERROR else State.IN_PROGRESS return result diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/FunctionCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/FunctionCommonizer.kt index 95e9a6dba7f..4792396ba9e 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/FunctionCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/FunctionCommonizer.kt @@ -10,12 +10,14 @@ import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ir.CirCommonFuncti import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ir.CirFunction class FunctionCommonizer(cache: CirClassifiersCache) : AbstractFunctionOrPropertyCommonizer(cache) { + private val annotations = AnnotationsCommonizer() private val modifiers = FunctionModifiersCommonizer.default() private val valueParameters = ValueParameterListCommonizer.default(cache) private var hasStableParameterNames = true private var hasSynthesizedParameterNames = false override fun commonizationResult() = CirCommonFunction( + annotations = annotations.result, name = name, modality = modality.result, visibility = visibility.result, @@ -31,6 +33,7 @@ class FunctionCommonizer(cache: CirClassifiersCache) : AbstractFunctionOrPropert override fun doCommonizeWith(next: CirFunction): Boolean { val result = super.doCommonizeWith(next) + && annotations.commonizeWith(next.annotations) && modifiers.commonizeWith(next) && valueParameters.commonizeWith(next.valueParameters) diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirAnnotation.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirAnnotation.kt index 62c12c36dfa..0cc6ddc2ca3 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirAnnotation.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirAnnotation.kt @@ -13,24 +13,17 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.constants.* -class CirAnnotation private constructor(original: AnnotationDescriptor) { - val fqName: FqName = original.fqName?.intern() ?: error("Annotation with no FQ name: ${original::class.java}, $original") - val allValueArguments: Map> = original.allValueArguments.mapKeys { it.key.intern() } - - init { - allValueArguments.forEach { (name, constantValue) -> - checkConstantSupportedInCommonization( - constantValue = constantValue, - constantName = name, - owner = original - ) - } - } - +class CirAnnotation private constructor( + val fqName: FqName, + val constantValueArguments: Map>, + val annotationValueArguments: Map +) { // See also org.jetbrains.kotlin.types.KotlinType.cachedHashCode private var cachedHashCode = 0 - private fun computeHashCode() = hashCode(fqName).appendHashCode(allValueArguments) + private fun computeHashCode() = hashCode(fqName) + .appendHashCode(constantValueArguments) + .appendHashCode(annotationValueArguments) override fun hashCode(): Int { var currentHashCode = cachedHashCode @@ -41,15 +34,61 @@ class CirAnnotation private constructor(original: AnnotationDescriptor) { return currentHashCode } - override fun equals(other: Any?): Boolean = when { - other === this -> true - other is CirAnnotation -> fqName == other.fqName && allValueArguments == other.allValueArguments - else -> false + override fun equals(other: Any?): Boolean { + if (other === this) return true + + return other is CirAnnotation + && fqName == other.fqName + && constantValueArguments == other.constantValueArguments + && annotationValueArguments == other.annotationValueArguments } companion object { private val interner = Interner() - fun create(original: AnnotationDescriptor): CirAnnotation = interner.intern(CirAnnotation(original)) + fun create(original: AnnotationDescriptor): CirAnnotation { + val fqName: FqName = original.fqName?.intern() ?: error("Annotation with no FQ name: ${original::class.java}, $original") + + val allValueArguments: Map> = original.allValueArguments + if (allValueArguments.isEmpty()) + return create(fqName = fqName, constantValueArguments = emptyMap(), annotationValueArguments = emptyMap()) + + val constantValueArguments: MutableMap> = HashMap() + val annotationValueArguments: MutableMap = HashMap() + + allValueArguments.forEach { (name, constantValue) -> + checkConstantSupportedInCommonization( + constantValue = constantValue, + constantName = name, + owner = original, + allowAnnotationValues = true + ) + + if (constantValue is AnnotationValue) + annotationValueArguments[name.intern()] = create(original = constantValue.value) + else + constantValueArguments[name.intern()] = constantValue + } + + return create( + fqName = fqName, + constantValueArguments = constantValueArguments, + annotationValueArguments = annotationValueArguments + ) + } + + fun create( + fqName: FqName, + constantValueArguments: Map>, + annotationValueArguments: Map + ): CirAnnotation { + return interner.intern( + CirAnnotation( + fqName = fqName, + constantValueArguments = constantValueArguments, + annotationValueArguments = annotationValueArguments + ) + ) + } } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirClass.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirClass.kt index f6278010f8f..563b6dd836e 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirClass.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirClass.kt @@ -40,7 +40,7 @@ data class CirCommonClass( override val isInline: Boolean, override val isInner: Boolean ) : CirClass { - override val annotations: List get() = emptyList() // TODO: commonize annotations, KT-34234 + override val annotations: List get() = emptyList() override val isData get() = false override val isExternal get() = false override var companion: FqName? = null @@ -56,7 +56,7 @@ data class CirCommonClassConstructor( override val hasStableParameterNames: Boolean, override val hasSynthesizedParameterNames: Boolean ) : CirClassConstructor { - override val annotations: List get() = emptyList() // TODO: commonize annotations, KT-34234 + override val annotations: List get() = emptyList() override val containingClassKind get() = unsupported() override val containingClassModality get() = unsupported() override val containingClassIsData get() = unsupported() diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirFunction.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirFunction.kt index 2d8876b0607..d29f4f2e5a2 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirFunction.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirFunction.kt @@ -30,6 +30,7 @@ interface CirCallableMemberWithParameters { interface CirFunction : CirFunctionOrProperty, CirFunctionModifiers, CirCallableMemberWithParameters data class CirCommonFunction( + override val annotations: List, override val name: Name, override val modality: Modality, override val visibility: Visibility, diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirFunctionOrProperty.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirFunctionOrProperty.kt index ecc872fef57..fc3be9c3d68 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirFunctionOrProperty.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirFunctionOrProperty.kt @@ -9,7 +9,8 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ir.CirExtensionReceiver.Companion.toReceiver import org.jetbrains.kotlin.descriptors.commonizer.utils.intern -interface CirFunctionOrProperty : CirAnnotatedDeclaration, CirNamedDeclaration, CirDeclarationWithTypeParameters, CirDeclarationWithVisibility, CirDeclarationWithModality, CirMaybeCallableMemberOfClass { +interface CirFunctionOrProperty : CirAnnotatedDeclaration, CirNamedDeclaration, CirDeclarationWithTypeParameters, + CirDeclarationWithVisibility, CirDeclarationWithModality, CirMaybeCallableMemberOfClass { val isExternal: Boolean val extensionReceiver: CirExtensionReceiver? val returnType: CirType @@ -17,7 +18,6 @@ interface CirFunctionOrProperty : CirAnnotatedDeclaration, CirNamedDeclaration, } abstract class CirCommonFunctionOrProperty : CirFunctionOrProperty { - final override val annotations: List get() = emptyList() // TODO: commonize annotations, KT-34234 final override val containingClassKind: ClassKind? get() = unsupported() final override val containingClassModality: Modality? get() = unsupported() final override val containingClassIsData: Boolean? get() = unsupported() @@ -50,8 +50,12 @@ data class CirExtensionReceiver( val type: CirType ) { companion object { - fun CirType.toReceiverNoAnnotations() = CirExtensionReceiver( /* TODO: commonize annotations, KT-34234 */ emptyList(), this) - fun ReceiverParameterDescriptor.toReceiver() = CirExtensionReceiver(annotations.map(CirAnnotation.Companion::create), CirType.create(type)) + fun CirType.toReceiverNoAnnotations() = CirExtensionReceiver(annotations = emptyList(), type = this) + + fun ReceiverParameterDescriptor.toReceiver() = CirExtensionReceiver( + annotations = annotations.map(CirAnnotation.Companion::create), + type = CirType.create(type) + ) } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirProperty.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirProperty.kt index bb6c80cb4bc..9b3f12f8cec 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirProperty.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/ir/CirProperty.kt @@ -36,6 +36,7 @@ data class CirCommonProperty( override val setter: CirSetter?, override val typeParameters: List ) : CirCommonFunctionOrProperty(), CirProperty { + override val annotations: List get() = emptyList() override val isVar get() = setter != null override val isLateInit get() = false override val isConst get() = false diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/blacklist.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/blacklist.kt index b59109b6a37..a52835c2004 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/blacklist.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/blacklist.kt @@ -7,11 +7,8 @@ package org.jetbrains.kotlin.descriptors.commonizer.utils import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.getAbbreviation -private val DEPRECATED_ANNOTATION_FQN = FqName(Deprecated::class.java.name) - internal fun SimpleFunctionDescriptor.isKniBridgeFunction() = name.asString().startsWith("kniBridge") diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/constants.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/constants.kt index 1765cb62fd4..e08ade191bf 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/constants.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/constants.kt @@ -12,11 +12,13 @@ internal fun checkConstantSupportedInCommonization( constantValue: ConstantValue<*>, constantName: Name? = null, owner: Any, + allowAnnotationValues: Boolean = false, onError: (String) -> Nothing = ::error ) { checkConstantSupportedInCommonization( constantValue = constantValue, location = { "${owner::class.java}, $owner" + constantName?.asString()?.let { "[$it]" } }, + allowAnnotationValues = allowAnnotationValues, onError = onError ) } @@ -24,6 +26,7 @@ internal fun checkConstantSupportedInCommonization( private fun checkConstantSupportedInCommonization( constantValue: ConstantValue<*>, location: () -> String, + allowAnnotationValues: Boolean, onError: (String) -> Nothing ) { @Suppress("TrailingComma") @@ -36,21 +39,28 @@ private fun checkConstantSupportedInCommonization( is DoubleValue, is FloatValue, is EnumValue -> { - // OK + return // OK } is AnnotationValue -> { - if (constantValue.value.fqName?.isUnderStandardKotlinPackages != true) - onError("Only ${constantValue::class.java} const values from Kotlin standard packages are supported, $constantValue at ${location()}") + if (allowAnnotationValues) { + if (constantValue.value.fqName?.isUnderStandardKotlinPackages != true) + onError("Only ${constantValue::class.java} const values from Kotlin standard packages are supported, $constantValue at ${location()}") + return // OK + } // else fail (see below) } is ArrayValue -> { constantValue.value.forEachIndexed { index, innerConstantValue -> checkConstantSupportedInCommonization( constantValue = innerConstantValue, location = { "${location()}[$index]" }, + allowAnnotationValues = allowAnnotationValues, onError = onError ) } + return // OK } - else -> onError("Unsupported const value type: ${constantValue::class.java}, $constantValue at ${location()}") } + + onError("Unsupported const value type: ${constantValue::class.java}, $constantValue at ${location()}") } + diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt index e32da0e9177..7e67c737bd5 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils/fqName.kt @@ -9,6 +9,8 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.serialization.konan.impl.ForwardDeclarationsFqNames +internal val DEPRECATED_ANNOTATION_FQN: FqName = FqName(Deprecated::class.java.name) + private val STANDARD_KOTLIN_PACKAGE_PREFIXES = listOf( KotlinBuiltIns.BUILT_INS_PACKAGE_NAME.asString(), "kotlinx" 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 a4445f4a970..362c9d6f9bf 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 @@ -13,6 +13,16 @@ internal fun Sequence.toList(expectedCapacity: Int): List { return result } +internal infix fun Map.concat(other: Map): Map = + when { + isEmpty() -> other + other.isEmpty() -> this + else -> HashMap(size + other.size, 1F).apply { + putAll(this@concat) + putAll(other) + } + } + internal inline fun Iterable.firstNonNull() = firstIsInstance() internal fun Any?.isNull(): Boolean = this == null