From 3111678f8b016d93635b801ac49a094ae0d27705 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Wed, 24 Jan 2024 11:19:33 +0100 Subject: [PATCH] [ObjCExport] Support nullable type arguments in translating known Collection types ^KT-65167 --- .../objcexport/translateToObjCMethod.kt | 45 +--------- .../objcexport/translateToObjCParameters.kt | 2 +- ...eferenceType.kt => translateToObjCType.kt} | 83 ++++++++++++++++--- 3 files changed, 74 insertions(+), 56 deletions(-) rename native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/{translateToObjCReferenceType.kt => translateToObjCType.kt} (59%) diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCMethod.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCMethod.kt index 6e5223c9c61..264ce97349a 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCMethod.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCMethod.kt @@ -6,7 +6,6 @@ import com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.annotations.annotationInfos import org.jetbrains.kotlin.analysis.api.symbols.* -import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi import org.jetbrains.kotlin.backend.konan.KonanFqNames import org.jetbrains.kotlin.backend.konan.objcexport.* @@ -14,7 +13,6 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.NameUtils import org.jetbrains.kotlin.objcexport.Predefined.anyMethodSelectors import org.jetbrains.kotlin.objcexport.analysisApiUtils.getFunctionMethodBridge -import org.jetbrains.kotlin.objcexport.analysisApiUtils.getInlineTargetTypeOrNull import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC import org.jetbrains.kotlin.psi.KtFile @@ -327,7 +325,7 @@ fun KtFunctionLikeSymbol.mapReturnType(returnBridge: MethodBridge.ReturnValue): MethodBridge.ReturnValue.Void, -> ObjCVoidType MethodBridge.ReturnValue.HashCode -> ObjCPrimitiveType.NSUInteger - is MethodBridge.ReturnValue.Mapped -> returnType.mapType(returnBridge.bridge) + is MethodBridge.ReturnValue.Mapped -> returnType.translateToObjCType(returnBridge.bridge) MethodBridge.ReturnValue.WithError.Success -> ObjCPrimitiveType.BOOL is MethodBridge.ReturnValue.WithError.ZeroForError -> { val successReturnType = mapReturnType(returnBridge.successBridge) @@ -349,44 +347,3 @@ fun KtFunctionLikeSymbol.mapReturnType(returnBridge: MethodBridge.ReturnValue): -> ObjCInstanceType } } - - -/** - * [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapType] - */ -context(KtAnalysisSession, KtObjCExportSession) -private fun KtType.mapType(typeBridge: TypeBridge): ObjCType { - - //if (!this.isObjCObjectType()) return null //TODO implement isObjCObjectType - - return when (typeBridge) { - is ReferenceBridge -> this.translateToObjCReferenceType() - is BlockPointerBridge -> this.translateToObjCFunctionType(typeBridge) - is ValueTypeBridge -> when (typeBridge.objCValueType) { - ObjCValueType.BOOL -> ObjCPrimitiveType.BOOL - ObjCValueType.UNICHAR -> ObjCPrimitiveType.unichar - ObjCValueType.CHAR -> ObjCPrimitiveType.int8_t - ObjCValueType.SHORT -> ObjCPrimitiveType.int16_t - ObjCValueType.INT -> ObjCPrimitiveType.int32_t - ObjCValueType.LONG_LONG -> ObjCPrimitiveType.int64_t - ObjCValueType.UNSIGNED_CHAR -> ObjCPrimitiveType.uint8_t - ObjCValueType.UNSIGNED_SHORT -> ObjCPrimitiveType.uint16_t - ObjCValueType.UNSIGNED_INT -> ObjCPrimitiveType.uint32_t - ObjCValueType.UNSIGNED_LONG_LONG -> ObjCPrimitiveType.uint64_t - ObjCValueType.FLOAT -> ObjCPrimitiveType.float - ObjCValueType.DOUBLE -> ObjCPrimitiveType.double - ObjCValueType.POINTER -> ObjCPointerType(ObjCVoidType, isBinaryRepresentationNullable()) - } - } -} - -context(KtAnalysisSession) -private fun KtType.isBinaryRepresentationNullable(): Boolean { - if (fullyExpandedType.isMarkedNullable) return true - - getInlineTargetTypeOrNull()?.let { inlineTargetType -> - if (inlineTargetType.isMarkedNullable) return true - } - - return false -} diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCParameters.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCParameters.kt index 918c5d51415..a7059220367 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCParameters.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCParameters.kt @@ -42,7 +42,7 @@ internal fun KtFunctionLikeSymbol.translateToObjCParameters(baseMethodBridge: Me val type = when (bridge) { is MethodBridgeValueParameter.Mapped -> - parameter!!.returnType.translateToObjCReferenceType() + parameter!!.returnType.translateToObjCType(bridge.bridge) MethodBridgeValueParameter.ErrorOutParameter -> ObjCPointerType(ObjCNullableReferenceType(ObjCClassType("NSError")), nullable = true) diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCReferenceType.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt similarity index 59% rename from native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCReferenceType.kt rename to native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt index 262999e2dce..3c7744d2a87 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCReferenceType.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCType.kt @@ -8,10 +8,53 @@ import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.objcexport.analysisApiUtils.getInlineTargetTypeOrNull import org.jetbrains.kotlin.objcexport.analysisApiUtils.isError import org.jetbrains.kotlin.objcexport.analysisApiUtils.objCErrorType + +/** + * [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapType] + */ +context(KtAnalysisSession, KtObjCExportSession) +internal fun KtType.translateToObjCType(typeBridge: TypeBridge): ObjCType { + + //if (!this.isObjCObjectType()) return null //TODO implement isObjCObjectType + + return when (typeBridge) { + is ReferenceBridge -> this.translateToObjCReferenceType() + is BlockPointerBridge -> this.translateToObjCFunctionType(typeBridge) + is ValueTypeBridge -> when (typeBridge.objCValueType) { + ObjCValueType.BOOL -> ObjCPrimitiveType.BOOL + ObjCValueType.UNICHAR -> ObjCPrimitiveType.unichar + ObjCValueType.CHAR -> ObjCPrimitiveType.int8_t + ObjCValueType.SHORT -> ObjCPrimitiveType.int16_t + ObjCValueType.INT -> ObjCPrimitiveType.int32_t + ObjCValueType.LONG_LONG -> ObjCPrimitiveType.int64_t + ObjCValueType.UNSIGNED_CHAR -> ObjCPrimitiveType.uint8_t + ObjCValueType.UNSIGNED_SHORT -> ObjCPrimitiveType.uint16_t + ObjCValueType.UNSIGNED_INT -> ObjCPrimitiveType.uint32_t + ObjCValueType.UNSIGNED_LONG_LONG -> ObjCPrimitiveType.uint64_t + ObjCValueType.FLOAT -> ObjCPrimitiveType.float + ObjCValueType.DOUBLE -> ObjCPrimitiveType.double + ObjCValueType.POINTER -> ObjCPointerType(ObjCVoidType, isBinaryRepresentationNullable()) + } + } +} + +context(KtAnalysisSession) +private fun KtType.isBinaryRepresentationNullable(): Boolean { + if (fullyExpandedType.isMarkedNullable) return true + + getInlineTargetTypeOrNull()?.let { inlineTargetType -> + if (inlineTargetType.isMarkedNullable) return true + } + + return false +} + + /** * [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapReferenceType] */ @@ -36,7 +79,7 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference return ObjCIdType } - if (classId in hiddenTypes) { + if (classId in hiddenClassIds) { return ObjCIdType } @@ -76,18 +119,30 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference val typeName = typesMap[classId] ?: classId!!.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName - val typeArguments = run { - if (this !is KtNonErrorClassType) { - return@run listOf() - } - - ownTypeArguments.mapNotNull { typeArgument -> typeArgument.type } - .map { typeArgumentType -> typeArgumentType.mapToReferenceTypeIgnoringNullability() } - } + val typeArguments = translateToObjCTypeArguments() return ObjCClassType(typeName, typeArguments) } + +context(KtAnalysisSession, KtObjCExportSession) +private fun KtType.translateToObjCTypeArguments(): List { + if (this !is KtNonErrorClassType) return emptyList() + + /* See special casing below */ + val isKnownCollectionType = classId in collectionClassIds + + return ownTypeArguments.mapNotNull { typeArgument -> typeArgument.type } + .map { typeArgumentType -> + + /* + Kotlin `null` keys and values are represented as `NSNull` singleton in collections + */ + if (isKnownCollectionType && typeArgumentType.isMarkedNullable) return@map ObjCIdType + typeArgumentType.mapToReferenceTypeIgnoringNullability() + } +} + private fun ObjCNonNullReferenceType.withNullabilityOf(kotlinType: KtType): ObjCReferenceType { return if (kotlinType.nullability.isNullable) { ObjCNullableReferenceType(this) @@ -102,7 +157,7 @@ private fun ObjCNonNullReferenceType.withNullabilityOf(kotlinType: KtType): ObjC * Currently contains super types of classes handled by custom type mappers. * Note: can be generated programmatically, but requires stdlib in this case. */ -private val hiddenTypes: Set = listOf( +private val hiddenClassIds: Set = listOf( "kotlin.Any", "kotlin.CharSequence", "kotlin.Comparable", @@ -112,4 +167,10 @@ private val hiddenTypes: Set = listOf( "kotlin.collections.Iterable", "kotlin.collections.MutableCollection", "kotlin.collections.MutableIterable" -).map { ClassId.topLevel(FqName(it)) }.toSet() \ No newline at end of file +).map { ClassId.topLevel(FqName(it)) }.toSet() + +private val collectionClassIds = setOf( + StandardClassIds.List, StandardClassIds.MutableList, + StandardClassIds.Set, StandardClassIds.MutableSet, + StandardClassIds.Map, StandardClassIds.MutableMap +) \ No newline at end of file