[ObjCExport] Support generic type parameters for ObjC translation

^KT-65167
This commit is contained in:
Sebastian Sellmair
2024-01-24 13:48:59 +01:00
committed by Space Team
parent 3111678f8b
commit 3cd20bd7b0
3 changed files with 93 additions and 26 deletions
@@ -293,23 +293,27 @@ fun MethodBridge.valueParametersAssociated(
function: KtFunctionLikeSymbol,
): List<Pair<MethodBridgeValueParameter, KtValueParameterSymbol?>> {
val allParameters = function.valueParameters.iterator()
if (!allParameters.hasNext()) return emptyList()
val skipFirstKotlinParameter = when (this.receiver) {
MethodBridgeReceiver.Static -> false
MethodBridgeReceiver.Factory, MethodBridgeReceiver.Instance -> true
}
if (skipFirstKotlinParameter && allParameters.hasNext()) {
allParameters.next()
MethodBridgeReceiver.Instance -> false
MethodBridgeReceiver.Factory -> true
}
return this.valueParameters.map {
when (it) {
is MethodBridgeValueParameter.Mapped -> it to allParameters.next()
val allParameters = function.valueParameters.let { valueParameters ->
if (skipFirstKotlinParameter) valueParameters.drop(1)
else valueParameters
}
if (allParameters.isEmpty()) return emptyList()
return this.valueParameters.mapIndexed { index, valueParameterBridge ->
when (valueParameterBridge) {
is MethodBridgeValueParameter.Mapped -> valueParameterBridge to allParameters[index]
is MethodBridgeValueParameter.SuspendCompletion,
is MethodBridgeValueParameter.ErrorOutParameter,
-> it to null
-> valueParameterBridge to null
}
}
}
@@ -1,9 +1,13 @@
package org.jetbrains.kotlin.objcexport
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection
import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.api.types.KtTypeParameterType
import org.jetbrains.kotlin.backend.konan.objcexport.*
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.name.ClassId
@@ -45,10 +49,10 @@ internal fun KtType.translateToObjCType(typeBridge: TypeBridge): ObjCType {
context(KtAnalysisSession)
private fun KtType.isBinaryRepresentationNullable(): Boolean {
if (fullyExpandedType.isMarkedNullable) return true
if (fullyExpandedType.canBeNull) return true
getInlineTargetTypeOrNull()?.let { inlineTargetType ->
if (inlineTargetType.isMarkedNullable) return true
if (inlineTargetType.canBeNull) return true
}
return false
@@ -116,12 +120,26 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference
}
}
val typeName = typesMap[classId]
?: classId!!.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName
if (fullyExpandedType is KtNonErrorClassType) {
val typeName = typesMap[classId]
?: fullyExpandedType.classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName
val typeArguments = translateToObjCTypeArguments()
val typeArguments = translateToObjCTypeArguments()
return ObjCClassType(typeName, typeArguments)
}
return ObjCClassType(typeName, typeArguments)
if (fullyExpandedType is KtTypeParameterType) {
if (fullyExpandedType.symbol.getContainingSymbol() is KtCallableSymbol) {
return ObjCIdType
}
/*
Todo: K1 has some name mangling logic here?
*/
return ObjCGenericTypeParameterUsage(fullyExpandedType.name.asString().toValidObjCSwiftIdentifier())
}
/* We cannot translate this, lets try to be lenient and emit the error type? */
return objCErrorType
}
@@ -132,19 +150,23 @@ private fun KtType.translateToObjCTypeArguments(): List<ObjCNonNullReferenceType
/* 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()
return ownTypeArguments.map { typeArgument ->
when (typeArgument) {
is KtStarTypeProjection -> ObjCIdType
is KtTypeArgumentWithVariance -> {
/*
Kotlin `null` keys and values are represented as `NSNull` singleton in collections
*/
if (isKnownCollectionType && typeArgument.type.isMarkedNullable) return@map ObjCIdType
typeArgument.type.mapToReferenceTypeIgnoringNullability()
}
}
}
}
context(KtAnalysisSession)
private fun ObjCNonNullReferenceType.withNullabilityOf(kotlinType: KtType): ObjCReferenceType {
return if (kotlinType.nullability.isNullable) {
return if (kotlinType.isBinaryRepresentationNullable()) {
ObjCNullableReferenceType(this)
} else {
this
@@ -403,6 +403,19 @@ class ObjCExportTypeTranslationTest(
assertEquals("T _Nullable -> void", header.renderTypesOfSymbol("foo"))
}
@Test
fun `test - class with generic function`() {
val header = header(
"""
class A {
fun <T: Any> foo(value: T) = Unit
}
""".trimIndent()
)
assertEquals("id -> void", header.renderTypesOfSymbol("foo"))
}
@Test
fun `test - generic class with bounds with function`() {
val header = header(
@@ -416,6 +429,34 @@ class ObjCExportTypeTranslationTest(
assertEquals("T -> void", header.renderTypesOfSymbol("foo"))
}
@Test
fun `test - nested classes with same type parameter`() {
val header = header(
"""
class A<T: Any> {
class B<T: Any> {
fun foo(value: T) = Unit
}
}
""".trimIndent()
)
assertEquals("T -> void", header.renderTypesOfSymbol("foo"))
}
@Test
fun `test - classes with same type parameter as function`() {
val header = header(
"""
class A<T: Any> {
fun <T: Any> foo(value: T) = Unit
}
""".trimIndent()
)
assertEquals("id -> void", header.renderTypesOfSymbol("foo"))
}
@Test
fun `test - unresolved error type`() {
val header = header(