[ObjCExport] Support generic type parameters for ObjC translation
^KT-65167
This commit is contained in:
committed by
Space Team
parent
3111678f8b
commit
3cd20bd7b0
+15
-11
@@ -293,23 +293,27 @@ fun MethodBridge.valueParametersAssociated(
|
|||||||
function: KtFunctionLikeSymbol,
|
function: KtFunctionLikeSymbol,
|
||||||
): List<Pair<MethodBridgeValueParameter, KtValueParameterSymbol?>> {
|
): List<Pair<MethodBridgeValueParameter, KtValueParameterSymbol?>> {
|
||||||
|
|
||||||
val allParameters = function.valueParameters.iterator()
|
|
||||||
if (!allParameters.hasNext()) return emptyList()
|
|
||||||
|
|
||||||
val skipFirstKotlinParameter = when (this.receiver) {
|
val skipFirstKotlinParameter = when (this.receiver) {
|
||||||
MethodBridgeReceiver.Static -> false
|
MethodBridgeReceiver.Static -> false
|
||||||
MethodBridgeReceiver.Factory, MethodBridgeReceiver.Instance -> true
|
MethodBridgeReceiver.Instance -> false
|
||||||
}
|
MethodBridgeReceiver.Factory -> true
|
||||||
if (skipFirstKotlinParameter && allParameters.hasNext()) {
|
|
||||||
allParameters.next()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.valueParameters.map {
|
val allParameters = function.valueParameters.let { valueParameters ->
|
||||||
when (it) {
|
if (skipFirstKotlinParameter) valueParameters.drop(1)
|
||||||
is MethodBridgeValueParameter.Mapped -> it to allParameters.next()
|
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.SuspendCompletion,
|
||||||
is MethodBridgeValueParameter.ErrorOutParameter,
|
is MethodBridgeValueParameter.ErrorOutParameter,
|
||||||
-> it to null
|
-> valueParameterBridge to null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-15
@@ -1,9 +1,13 @@
|
|||||||
package org.jetbrains.kotlin.objcexport
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
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.symbols.KtNamedClassOrObjectSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
|
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
|
||||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
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.backend.konan.objcexport.*
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
@@ -45,10 +49,10 @@ internal fun KtType.translateToObjCType(typeBridge: TypeBridge): ObjCType {
|
|||||||
|
|
||||||
context(KtAnalysisSession)
|
context(KtAnalysisSession)
|
||||||
private fun KtType.isBinaryRepresentationNullable(): Boolean {
|
private fun KtType.isBinaryRepresentationNullable(): Boolean {
|
||||||
if (fullyExpandedType.isMarkedNullable) return true
|
if (fullyExpandedType.canBeNull) return true
|
||||||
|
|
||||||
getInlineTargetTypeOrNull()?.let { inlineTargetType ->
|
getInlineTargetTypeOrNull()?.let { inlineTargetType ->
|
||||||
if (inlineTargetType.isMarkedNullable) return true
|
if (inlineTargetType.canBeNull) return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@@ -116,12 +120,26 @@ private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReference
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val typeName = typesMap[classId]
|
if (fullyExpandedType is KtNonErrorClassType) {
|
||||||
?: classId!!.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName
|
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 */
|
/* See special casing below */
|
||||||
val isKnownCollectionType = classId in collectionClassIds
|
val isKnownCollectionType = classId in collectionClassIds
|
||||||
|
|
||||||
return ownTypeArguments.mapNotNull { typeArgument -> typeArgument.type }
|
return ownTypeArguments.map { typeArgument ->
|
||||||
.map { typeArgumentType ->
|
when (typeArgument) {
|
||||||
|
is KtStarTypeProjection -> ObjCIdType
|
||||||
/*
|
is KtTypeArgumentWithVariance -> {
|
||||||
Kotlin `null` keys and values are represented as `NSNull` singleton in collections
|
/*
|
||||||
*/
|
Kotlin `null` keys and values are represented as `NSNull` singleton in collections
|
||||||
if (isKnownCollectionType && typeArgumentType.isMarkedNullable) return@map ObjCIdType
|
*/
|
||||||
typeArgumentType.mapToReferenceTypeIgnoringNullability()
|
if (isKnownCollectionType && typeArgument.type.isMarkedNullable) return@map ObjCIdType
|
||||||
|
typeArgument.type.mapToReferenceTypeIgnoringNullability()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context(KtAnalysisSession)
|
||||||
private fun ObjCNonNullReferenceType.withNullabilityOf(kotlinType: KtType): ObjCReferenceType {
|
private fun ObjCNonNullReferenceType.withNullabilityOf(kotlinType: KtType): ObjCReferenceType {
|
||||||
return if (kotlinType.nullability.isNullable) {
|
return if (kotlinType.isBinaryRepresentationNullable()) {
|
||||||
ObjCNullableReferenceType(this)
|
ObjCNullableReferenceType(this)
|
||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
|
|||||||
+41
@@ -403,6 +403,19 @@ class ObjCExportTypeTranslationTest(
|
|||||||
assertEquals("T _Nullable -> void", header.renderTypesOfSymbol("foo"))
|
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
|
@Test
|
||||||
fun `test - generic class with bounds with function`() {
|
fun `test - generic class with bounds with function`() {
|
||||||
val header = header(
|
val header = header(
|
||||||
@@ -416,6 +429,34 @@ class ObjCExportTypeTranslationTest(
|
|||||||
assertEquals("T -> void", header.renderTypesOfSymbol("foo"))
|
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
|
@Test
|
||||||
fun `test - unresolved error type`() {
|
fun `test - unresolved error type`() {
|
||||||
val header = header(
|
val header = header(
|
||||||
|
|||||||
Reference in New Issue
Block a user