From 6b6808d87c15fd099f61cc4d9003f14ede6239fe Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Wed, 21 Feb 2024 13:37:33 +0100 Subject: [PATCH] [ObjCExport] ObjCType: Implement HasExtras instead of HasMutableExtras ... to enforce the immutability concept of this entities. KT-65891 Fixed --- .../objcexport/analysisApiUtils/errors.kt | 7 +++-- .../objcexport/buildCompanionProperty.kt | 12 ++++---- .../objcexport/extras/objCTypeExtras.kt | 26 +++++++++++++++++ .../kotlin/objcexport/extras/originClassId.kt | 17 ++++++----- .../extras/requiresForwardDeclaration.kt | 13 +++++---- .../objcexport/translateToObjCObject.kt | 15 +++++++--- .../objcexport/translateToObjCObjectType.kt | 15 +++++++--- .../kotlin/objcexport/translateToObjCType.kt | 26 ++++++++++++----- .../backend/konan/objcexport/objcTypes.kt | 28 ++++++++++++++----- 9 files changed, 118 insertions(+), 41 deletions(-) create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/objCTypeExtras.kt diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt index e62021b80d1..ddacf82ec1b 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/analysisApiUtils/errors.kt @@ -4,7 +4,8 @@ import org.jetbrains.kotlin.analysis.api.types.KtClassErrorType import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.objcexport.KtObjCExportSession -import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration +import org.jetbrains.kotlin.objcexport.extras.objCTypeExtras +import org.jetbrains.kotlin.objcexport.extras.requiresForwardDeclaration /** * Traverses stubs and returns true if [objCErrorType] is used as a return, parameter or property type @@ -45,4 +46,6 @@ internal val errorInterface superClassGenerics = emptyList() ) -internal val objCErrorType = ObjCClassType(errorClassName).withRequiresForwardDeclaration() +internal val objCErrorType = ObjCClassType(errorClassName, extras = objCTypeExtras { + requiresForwardDeclaration = true +}) diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/buildCompanionProperty.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/buildCompanionProperty.kt index 8e35a39d87d..4c7802291d1 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/buildCompanionProperty.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/buildCompanionProperty.kt @@ -6,8 +6,9 @@ import org.jetbrains.kotlin.backend.konan.objcexport.ObjCClassType import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProperty import org.jetbrains.kotlin.backend.konan.objcexport.swiftNameAttribute import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCompanion -import org.jetbrains.kotlin.objcexport.extras.withOriginClassId -import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration +import org.jetbrains.kotlin.objcexport.extras.objCTypeExtras +import org.jetbrains.kotlin.objcexport.extras.originClassId +import org.jetbrains.kotlin.objcexport.extras.requiresForwardDeclaration /** * If object class has companion object it needs to have property which returns this companion. @@ -25,9 +26,10 @@ internal fun KtClassOrObjectSymbol.buildCompanionProperty(): ObjCProperty { name = propertyName, comment = null, origin = null, - type = ObjCClassType(typeName.objCName) - .withRequiresForwardDeclaration() - .withOriginClassId(companion.classIdIfNonLocal), + type = ObjCClassType(typeName.objCName, extras = objCTypeExtras { + requiresForwardDeclaration = true + originClassId = companion.classIdIfNonLocal + }), propertyAttributes = listOf("class", "readonly"), getterName = propertyName, declarationAttributes = listOf(swiftNameAttribute(propertyName)) diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/objCTypeExtras.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/objCTypeExtras.kt new file mode 100644 index 00000000000..06e9ea6b8ba --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/objCTypeExtras.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2024 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.objcexport.extras + +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCType +import org.jetbrains.kotlin.tooling.core.Extras +import org.jetbrains.kotlin.tooling.core.MutableExtras +import org.jetbrains.kotlin.tooling.core.mutableExtrasOf + +/** + * Acts as scope for discoverable 'builder functions' + * - See [objCTypeExtras] factory function + * - See [MutableExtras.originClassId] as example for declaring a discoverable API for types + * - See [MutableExtras.requiresForwardDeclaration] "=" + */ +internal object ObjCTypeExtrasBuilderContext + +/** + * Convenience function for building extras for [ObjCType] + */ +internal fun objCTypeExtras(builder: context(ObjCTypeExtrasBuilderContext) MutableExtras.() -> Unit): Extras { + return mutableExtrasOf().also { extras -> builder(ObjCTypeExtrasBuilderContext, extras) } +} diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/originClassId.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/originClassId.kt index c78771efc6a..62a4bb01e40 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/originClassId.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/originClassId.kt @@ -6,12 +6,13 @@ package org.jetbrains.kotlin.objcexport.extras import org.jetbrains.kotlin.backend.konan.objcexport.ObjCNullableReferenceType -import org.jetbrains.kotlin.backend.konan.objcexport.ObjCReferenceType import org.jetbrains.kotlin.backend.konan.objcexport.ObjCType import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.tooling.core.MutableExtras import org.jetbrains.kotlin.tooling.core.extrasKeyOf -private val originClassIdKey = extrasKeyOf() + +private val originClassIdKey = extrasKeyOf() /** * Tracks the Kotlin origin associated with [this] [ObjCType]. @@ -30,9 +31,11 @@ internal val ObjCType.originClassId: ClassId? } /** - * See [originClassId] + * See [ObjCType.originClassId] */ -internal fun T.withOriginClassId(classId: ClassId?): T = also { type -> - if (classId != null) type.extras[originClassIdKey] = classId - else type.extras.remove(originClassIdKey) -} +context(ObjCTypeExtrasBuilderContext) +internal var MutableExtras.originClassId: ClassId? + get() = this[originClassIdKey] + set(value) { + this[originClassIdKey] = value + } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/requiresForwardDeclaration.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/requiresForwardDeclaration.kt index 8daea935e3c..4981571ed04 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/requiresForwardDeclaration.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/extras/requiresForwardDeclaration.kt @@ -6,8 +6,8 @@ package org.jetbrains.kotlin.objcexport.extras import org.jetbrains.kotlin.backend.konan.objcexport.ObjCReferenceType +import org.jetbrains.kotlin.tooling.core.MutableExtras import org.jetbrains.kotlin.tooling.core.extrasKeyOf -import org.jetbrains.kotlin.tooling.core.readWriteProperty private val requiresForwardDeclarationKey = extrasKeyOf("isForwardDeclaration") @@ -16,13 +16,16 @@ private val requiresForwardDeclarationKey = extrasKeyOf("isForwardDecla * - Default value: `false`. * - Example: All types used in function and method signature are expected to render forward declarations */ -internal val ObjCReferenceType.requiresForwardDeclaration by requiresForwardDeclarationKey.readWriteProperty.notNull(false) +internal val ObjCReferenceType.requiresForwardDeclaration: Boolean get() = extras[requiresForwardDeclarationKey] ?: false /** * ⚠️ Marks [this] [ObjCReferenceType] as 'requires forward declaration' and returns the same instance. * This method shall be used during the construction of a new type. * @see ObjCReferenceType.requiresForwardDeclaration */ -internal fun T.withRequiresForwardDeclaration(): T = also { type -> - type.extras[requiresForwardDeclarationKey] = true -} +context(ObjCTypeExtrasBuilderContext) +internal var MutableExtras.requiresForwardDeclaration: Boolean + get() = this[requiresForwardDeclarationKey] ?: false + set(value) { + this[requiresForwardDeclarationKey] = value + } diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt index 9fac38f3e2a..95e065c22dd 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObject.kt @@ -8,8 +8,9 @@ import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCompanion import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC -import org.jetbrains.kotlin.objcexport.extras.withOriginClassId -import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration +import org.jetbrains.kotlin.objcexport.extras.objCTypeExtras +import org.jetbrains.kotlin.objcexport.extras.originClassId +import org.jetbrains.kotlin.objcexport.extras.requiresForwardDeclaration context(KtAnalysisSession, KtObjCExportSession) fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? { @@ -85,8 +86,14 @@ private fun KtClassOrObjectSymbol.getDefaultMembers(): List { * See also: [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapReferenceType] */ context(KtAnalysisSession, KtObjCExportSession) -private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType(getObjCClassOrProtocolName().objCName, emptyList(),) - .withRequiresForwardDeclaration().withOriginClassId(classIdIfNonLocal) +private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType( + className = getObjCClassOrProtocolName().objCName, + typeArguments = emptyList(), + extras = objCTypeExtras { + requiresForwardDeclaration = true + originClassId = classIdIfNonLocal + } +) /** * [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getObjectInstanceSelector] diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObjectType.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObjectType.kt index ba69ebc6a52..19c2c500c71 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObjectType.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCObjectType.kt @@ -21,7 +21,8 @@ import org.jetbrains.kotlin.name.NativeStandardInteropNames.cInteropPackage import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDeclaredSuperInterfaceSymbols import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny import org.jetbrains.kotlin.objcexport.analysisApiUtils.objCErrorType -import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration +import org.jetbrains.kotlin.objcexport.extras.objCTypeExtras +import org.jetbrains.kotlin.objcexport.extras.requiresForwardDeclaration /** * ClassId for 'kotlinx.cinterop.ObjCClass' @@ -49,13 +50,19 @@ internal fun KtType.translateToObjCObjectType(): ObjCNonNullReferenceType { context(KtAnalysisSession, KtObjCExportSession) private fun KtClassOrObjectSymbol.translateToObjCObjectType(): ObjCNonNullReferenceType { if (isObjCMetaClass()) return ObjCMetaClassType - if (isObjCProtocolClass()) return ObjCClassType("Protocol").withRequiresForwardDeclaration() + if (isObjCProtocolClass()) return ObjCClassType("Protocol", extras = objCTypeExtras { + requiresForwardDeclaration = true + }) if (isExternalObjCClass() || isObjCForwardDeclaration()) { return if (classKind == KtClassKind.INTERFACE) { - ObjCProtocolType(nameOrAnonymous.asString().removeSuffix("Protocol")).withRequiresForwardDeclaration() + ObjCProtocolType(nameOrAnonymous.asString().removeSuffix("Protocol"), extras = objCTypeExtras { + requiresForwardDeclaration = true + }) } else { - ObjCClassType(nameOrAnonymous.asString()).withRequiresForwardDeclaration() + ObjCClassType(nameOrAnonymous.asString(), extras = objCTypeExtras { + requiresForwardDeclaration = true + }) } } diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt index 3022e892f1f..19be7ac0cb6 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt @@ -19,8 +19,9 @@ import org.jetbrains.kotlin.objcexport.analysisApiUtils.getInlineTargetTypeOrNul import org.jetbrains.kotlin.objcexport.analysisApiUtils.isError import org.jetbrains.kotlin.objcexport.analysisApiUtils.isObjCObjectType import org.jetbrains.kotlin.objcexport.analysisApiUtils.objCErrorType -import org.jetbrains.kotlin.objcexport.extras.withOriginClassId -import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration +import org.jetbrains.kotlin.objcexport.extras.objCTypeExtras +import org.jetbrains.kotlin.objcexport.extras.originClassId +import org.jetbrains.kotlin.objcexport.extras.requiresForwardDeclaration /** @@ -106,13 +107,24 @@ internal fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReferenc } if (fullyExpandedType is KtNonErrorClassType) { - - // TODO NOW: create type translation test return if (classSymbol?.classKind == KtClassKind.INTERFACE) { - ObjCProtocolType(fullyExpandedType.objCTypeName) + ObjCProtocolType( + protocolName = fullyExpandedType.objCTypeName, + extras = objCTypeExtras { + requiresForwardDeclaration = true + originClassId = classId + } + ) } else { - ObjCClassType(fullyExpandedType.objCTypeName, translateTypeArgumentsToObjC()) - }.withRequiresForwardDeclaration().withOriginClassId(classId) + ObjCClassType( + className = fullyExpandedType.objCTypeName, + typeArguments = translateTypeArgumentsToObjC(), + extras = objCTypeExtras { + requiresForwardDeclaration = true + originClassId = classId + } + ) + } } if (fullyExpandedType is KtTypeParameterType) { diff --git a/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt index df749dfa7e9..42cb3f54eee 100644 --- a/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt +++ b/native/objcexport-header-generator/src/org/jetbrains/kotlin/backend/konan/objcexport/objcTypes.kt @@ -6,14 +6,12 @@ package org.jetbrains.kotlin.backend.konan.objcexport import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi -import org.jetbrains.kotlin.tooling.core.HasMutableExtras -import org.jetbrains.kotlin.tooling.core.MutableExtras -import org.jetbrains.kotlin.tooling.core.mutableExtrasOf +import org.jetbrains.kotlin.tooling.core.Extras +import org.jetbrains.kotlin.tooling.core.HasExtras +import org.jetbrains.kotlin.tooling.core.emptyExtras import org.jetbrains.kotlin.types.Variance -sealed class ObjCType : HasMutableExtras { - override val extras: MutableExtras = mutableExtrasOf() - +sealed class ObjCType : HasExtras { final override fun toString(): String = this.render() abstract fun render(attrsAndName: String): String @@ -26,6 +24,7 @@ sealed class ObjCType : HasMutableExtras { data class ObjCRawType( val rawText: String, + override val extras: Extras = emptyExtras() ) : ObjCType() { override fun render(attrsAndName: String): String = rawText.withAttrsAndName(attrsAndName) } @@ -36,6 +35,7 @@ sealed class ObjCNonNullReferenceType : ObjCReferenceType() data class ObjCNullableReferenceType( val nonNullType: ObjCNonNullReferenceType, val isNullableResult: Boolean = false, + override val extras: Extras = emptyExtras(), ) : ObjCReferenceType() { override fun render(attrsAndName: String): String { val attribute = if (isNullableResult) objcNullableResultAttribute else objcNullableAttribute @@ -46,6 +46,7 @@ data class ObjCNullableReferenceType( data class ObjCClassType( val className: String, val typeArguments: List = emptyList(), + override val extras: Extras = emptyExtras() ) : ObjCNonNullReferenceType() { @@ -68,29 +69,37 @@ sealed class ObjCGenericTypeUsage : ObjCNonNullReferenceType() { } } -data class ObjCGenericTypeRawUsage(override val typeName: String) : ObjCGenericTypeUsage() +data class ObjCGenericTypeRawUsage( + override val typeName: String, + override val extras: Extras = emptyExtras() +) : ObjCGenericTypeUsage() data class ObjCGenericTypeParameterUsage( override val typeName: String, + override val extras: Extras = emptyExtras() ) : ObjCGenericTypeUsage() data class ObjCProtocolType( val protocolName: String, + override val extras: Extras = emptyExtras() ) : ObjCNonNullReferenceType() { override fun render(attrsAndName: String) = "id<$protocolName>".withAttrsAndName(attrsAndName) } object ObjCIdType : ObjCNonNullReferenceType() { + override val extras: Extras = emptyExtras() override fun render(attrsAndName: String) = "id".withAttrsAndName(attrsAndName) } object ObjCInstanceType : ObjCNonNullReferenceType() { + override val extras: Extras = emptyExtras() override fun render(attrsAndName: String): String = "instancetype".withAttrsAndName(attrsAndName) } data class ObjCBlockPointerType( val returnType: ObjCType, val parameterTypes: List, + override val extras: Extras = emptyExtras() ) : ObjCNonNullReferenceType() { override fun render(attrsAndName: String) = returnType.render(buildString { append("(^") @@ -103,12 +112,15 @@ data class ObjCBlockPointerType( } object ObjCMetaClassType : ObjCNonNullReferenceType() { + override val extras: Extras = emptyExtras() override fun render(attrsAndName: String): String = "Class".withAttrsAndName(attrsAndName) } sealed class ObjCPrimitiveType( val cName: String, ) : ObjCType() { + override val extras: Extras = emptyExtras() + object NSUInteger : ObjCPrimitiveType("NSUInteger") object BOOL : ObjCPrimitiveType("BOOL") object unichar : ObjCPrimitiveType("unichar") @@ -140,6 +152,7 @@ sealed class ObjCPrimitiveType( data class ObjCPointerType( val pointee: ObjCType, val nullable: Boolean = false, + override val extras: Extras = emptyExtras() ) : ObjCType() { override fun render(attrsAndName: String) = pointee.render( @@ -154,6 +167,7 @@ data class ObjCPointerType( } object ObjCVoidType : ObjCType() { + override val extras: Extras = emptyExtras() override fun render(attrsAndName: String) = "void".withAttrsAndName(attrsAndName) }