[ObjCExport] Add inner classes translation
KT-66339
This commit is contained in:
committed by
Space Team
parent
5fee662223
commit
f7f779df53
+9
-12
@@ -48,12 +48,15 @@ internal fun KtVariableLikeSymbol.getPropertyMethodBridge(): MethodBridge {
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private val KtCallableSymbol.receiverType: MethodBridgeReceiver
|
||||
get() = if (isArrayConstructor) {
|
||||
MethodBridgeReceiver.Factory
|
||||
} else if (!isConstructor && isTopLevel && !isExtension) {
|
||||
MethodBridgeReceiver.Static
|
||||
} else {
|
||||
MethodBridgeReceiver.Instance
|
||||
get() {
|
||||
val isProperty = getContainingSymbol() as? KtPropertySymbol != null
|
||||
return if (isArrayConstructor) {
|
||||
MethodBridgeReceiver.Factory
|
||||
} else if (!isConstructor && isTopLevel && !isExtension && !isProperty) {
|
||||
MethodBridgeReceiver.Static
|
||||
} else {
|
||||
MethodBridgeReceiver.Instance
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,12 +151,6 @@ private fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
|
||||
return MethodBridge.ReturnValue.HashCode
|
||||
}
|
||||
|
||||
//TODO: handle getter
|
||||
// descriptor is PropertyGetterDescriptor -> {
|
||||
// assert(!convertExceptionsToErrors)
|
||||
// MethodBridge.ReturnValue.Mapped(bridgePropertyType(descriptor.correspondingProperty))
|
||||
// }
|
||||
|
||||
if (returnType.isUnit || returnType.isNothing) {
|
||||
return if (hasThrowsAnnotation) {
|
||||
MethodBridge.ReturnValue.WithError.Success
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.analysisApiUtils
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertyAccessorSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
||||
import org.jetbrains.kotlin.tooling.core.linearClosure
|
||||
|
||||
context(KtAnalysisSession)
|
||||
internal fun KtPropertyAccessorSymbol.getPropertySymbol(): KtPropertySymbol {
|
||||
return this.linearClosure<KtSymbol> { it.getContainingSymbol() }.filterIsInstance<KtPropertySymbol>().firstOrNull()
|
||||
?: error("Missing '${KtPropertySymbol::class} on ${this.render()}")
|
||||
}
|
||||
+29
-7
@@ -3,6 +3,7 @@ package org.jetbrains.kotlin.objcexport
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportFunctionName
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getPropertySymbol
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFunctionLikeSymbol.getObjCFunctionName(): ObjCExportFunctionName {
|
||||
@@ -13,20 +14,41 @@ fun KtFunctionLikeSymbol.getObjCFunctionName(): ObjCExportFunctionName {
|
||||
)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun KtFunctionLikeSymbol.getObjCName(resolvedNameAnnotation: KtResolvedObjCNameAnnotation?): String {
|
||||
return resolvedNameAnnotation?.objCName ?: translationName
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun KtFunctionLikeSymbol.getSwiftName(resolvedNameAnnotation: KtResolvedObjCNameAnnotation?): String {
|
||||
return resolvedNameAnnotation?.swiftName ?: translationName
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private val KtFunctionLikeSymbol.translationName: String
|
||||
get() = when (this) {
|
||||
is KtFunctionSymbol -> name.asString()
|
||||
is KtConstructorSymbol -> "init"
|
||||
is KtPropertyGetterSymbol -> ""
|
||||
is KtAnonymousFunctionSymbol -> ""
|
||||
is KtPropertySetterSymbol -> ""
|
||||
is KtSamConstructorSymbol -> ""
|
||||
get() {
|
||||
return when (this) {
|
||||
is KtFunctionSymbol -> name.asString()
|
||||
is KtConstructorSymbol -> "init"
|
||||
is KtPropertyAccessorSymbol -> this.objCPropertyName
|
||||
is KtAnonymousFunctionSymbol -> ""
|
||||
is KtSamConstructorSymbol -> ""
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private val KtPropertyAccessorSymbol.objCPropertyName: String
|
||||
get() {
|
||||
return if (!this.getPropertySymbol().isObjCProperty) {
|
||||
val containingSymbol = getContainingSymbol()
|
||||
when (this) {
|
||||
is KtPropertyGetterSymbol -> {
|
||||
(containingSymbol as KtPropertySymbol).name.asString()
|
||||
}
|
||||
is KtPropertySetterSymbol -> {
|
||||
"set" + ((containingSymbol as KtPropertySymbol).name.asString()).replaceFirstChar(kotlin.Char::uppercaseChar)
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
} else ""
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.tooling.core.linearClosure
|
||||
|
||||
/**
|
||||
* Property needs to be translated and support special naming in 2 cases:
|
||||
* 1. It has no receiver type and no extension
|
||||
* 2. It is a property of an inner class
|
||||
*
|
||||
* See K1 [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.isObjCProperty]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
internal val KtPropertySymbol.isObjCProperty: Boolean
|
||||
get() {
|
||||
val hasReceiver = receiverParameter != null && !isExtension
|
||||
return !hasReceiver && !isPropertyInInnerClass
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private val KtPropertySymbol.isPropertyInInnerClass: Boolean
|
||||
get() = linearClosure<KtSymbol> { symbol -> symbol.getContainingSymbol() }
|
||||
.any { it is KtNamedClassOrObjectSymbol && it.isInner }
|
||||
+1
-1
@@ -36,7 +36,7 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
|
||||
this += getCallableSymbolsForObjCMemberTranslation()
|
||||
.sortedWith(StableCallableOrder)
|
||||
.mapNotNull { it.translateToObjCExportStub() }
|
||||
.flatMap { it.translateToObjCExportStub() }
|
||||
|
||||
if (classKind == KtClassKind.ENUM_CLASS) {
|
||||
this += translateEnumMembers()
|
||||
|
||||
+15
-5
@@ -4,14 +4,24 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCClass
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtCallableSymbol.translateToObjCExportStub(): ObjCExportStub? {
|
||||
return when (this) {
|
||||
is KtPropertySymbol -> translateToObjCProperty()
|
||||
is KtFunctionSymbol -> translateToObjCMethod()
|
||||
else -> null
|
||||
internal fun KtCallableSymbol.translateToObjCExportStub(): List<ObjCExportStub> {
|
||||
val result = mutableListOf<ObjCExportStub>()
|
||||
when (this) {
|
||||
is KtPropertySymbol -> {
|
||||
if (isObjCProperty) {
|
||||
result.addIfNotNull(translateToObjCProperty())
|
||||
} else {
|
||||
result.addIfNotNull(this.getter?.translateToObjCMethod())
|
||||
result.addIfNotNull(this.setter?.translateToObjCMethod())
|
||||
}
|
||||
}
|
||||
is KtFunctionSymbol -> result.addIfNotNull(translateToObjCMethod())
|
||||
else -> Unit
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ fun KtResolvedObjCExportFile.translateToObjCExtensionFacades(): List<ObjCInterfa
|
||||
origin = null,
|
||||
attributes = emptyList(),
|
||||
superProtocols = emptyList(),
|
||||
members = extensionSymbols.mapNotNull { ext -> ext.translateToObjCExportStub() },
|
||||
members = extensionSymbols.flatMap { ext -> ext.translateToObjCExportStub() },
|
||||
categoryName = extensionsCategoryName,
|
||||
generics = emptyList(),
|
||||
superClass = null,
|
||||
|
||||
+13
-32
@@ -6,7 +6,6 @@ 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.symbols.markers.KtNamedSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -20,10 +19,10 @@ internal val KtCallableSymbol.isConstructor: Boolean
|
||||
get() = this is KtConstructorSymbol
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFunctionSymbol.translateToObjCMethod(): ObjCMethod? {
|
||||
fun KtFunctionLikeSymbol.translateToObjCMethod(): ObjCMethod? {
|
||||
if (!isVisibleInObjC()) return null
|
||||
if (isFakeOverride) return null
|
||||
if (isClone) return null
|
||||
if (this is KtFunctionSymbol && isClone) return null
|
||||
return buildObjCMethod()
|
||||
}
|
||||
|
||||
@@ -48,7 +47,7 @@ internal fun KtFunctionLikeSymbol.buildObjCMethod(
|
||||
val returnType: ObjCType = mapReturnType(bridge.returnBridge)
|
||||
val parameters = translateToObjCParameters(bridge)
|
||||
val selector = getSelector(bridge)
|
||||
val selectors: List<String> = splitSelector(selector)
|
||||
val selectors = splitSelector(selector)
|
||||
val swiftName = getSwiftName(bridge)
|
||||
val attributes = mutableListOf<String>()
|
||||
val returnBridge = bridge.returnBridge
|
||||
@@ -127,15 +126,18 @@ internal fun KtFunctionLikeSymbol.getSwiftName(methodBridge: MethodBridge): Stri
|
||||
append(getMangledName(forSwift = true))
|
||||
append("(")
|
||||
|
||||
parameters@ for ((bridge, symbol) in parameters) {
|
||||
parameters@ for ((bridge, parameter: KtObjCParameterData?) in parameters) {
|
||||
val label = when (bridge) {
|
||||
is MethodBridgeValueParameter.Mapped -> when {
|
||||
//it is ReceiverParameterDescriptor -> it.getObjCName().asIdentifier(true) { "_" }
|
||||
parameter?.isReceiver == true -> "_"
|
||||
method is KtPropertySetterSymbol -> when (parameters.size) {
|
||||
1 -> "_"
|
||||
else -> "value"
|
||||
}
|
||||
else -> symbol!!.name
|
||||
else -> {
|
||||
if (parameter == null) continue@parameters
|
||||
else if (parameter.isReceiver) "_" else parameter.name
|
||||
}
|
||||
}
|
||||
MethodBridgeValueParameter.ErrorOutParameter -> continue@parameters
|
||||
is MethodBridgeValueParameter.SuspendCompletion -> "completionHandler"
|
||||
@@ -149,7 +151,6 @@ internal fun KtFunctionLikeSymbol.getSwiftName(methodBridge: MethodBridge): Stri
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -190,23 +191,23 @@ fun KtFunctionLikeSymbol.getSelector(methodBridge: MethodBridge): String {
|
||||
}
|
||||
|
||||
val parameters = methodBridge.valueParametersAssociated(this)
|
||||
|
||||
val method = this
|
||||
|
||||
val sb = StringBuilder()
|
||||
|
||||
sb.append(method.getMangledName(forSwift = false))
|
||||
|
||||
parameters.forEachIndexed { index, (bridge, typeParameterSymbol) ->
|
||||
parameters.forEachIndexed { index, (bridge, parameter) ->
|
||||
val name = when (bridge) {
|
||||
|
||||
is MethodBridgeValueParameter.Mapped -> when {
|
||||
parameter?.isReceiver == true -> ""
|
||||
method is KtPropertySetterSymbol -> when (parameters.size) {
|
||||
1 -> ""
|
||||
else -> "value"
|
||||
}
|
||||
else -> {
|
||||
typeParameterSymbol!!.name.toString()
|
||||
if (parameter == null) return@forEachIndexed
|
||||
else if (parameter.isReceiver) "" else parameter.name.toString()
|
||||
}
|
||||
}
|
||||
MethodBridgeValueParameter.ErrorOutParameter -> "error"
|
||||
@@ -255,26 +256,6 @@ private fun String.handleSpecialNames(prefix: String): String {
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.MethodBrideExtensionsKt.valueParametersAssociated]
|
||||
*/
|
||||
@InternalKotlinNativeApi
|
||||
fun MethodBridge.valueParametersAssociated(
|
||||
function: KtFunctionLikeSymbol,
|
||||
): List<Pair<MethodBridgeValueParameter, KtValueParameterSymbol?>> {
|
||||
val allParameters = function.valueParameters
|
||||
|
||||
return this.valueParameters.mapIndexed { index, valueParameterBridge ->
|
||||
when (valueParameterBridge) {
|
||||
is MethodBridgeValueParameter.Mapped -> valueParameterBridge to allParameters[index]
|
||||
|
||||
is MethodBridgeValueParameter.SuspendCompletion,
|
||||
is MethodBridgeValueParameter.ErrorOutParameter,
|
||||
-> valueParameterBridge to null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.startsWithWords(words: String) = this.startsWith(words) &&
|
||||
(this.length == words.length || !this[words.length].isLowerCase())
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? {
|
||||
objectMembers += getDefaultMembers()
|
||||
objectMembers += getDeclaredMemberScope().getCallableSymbols()
|
||||
.sortedWith(StableCallableOrder)
|
||||
.mapNotNull { it.translateToObjCExportStub() }
|
||||
.flatMap { it.translateToObjCExportStub() }
|
||||
|
||||
return ObjCInterfaceImpl(
|
||||
name = name.objCName,
|
||||
|
||||
+13
-9
@@ -5,11 +5,9 @@ import org.jetbrains.kotlin.analysis.api.components.buildClassType
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySetterSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.cKeywords
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtFunctionLikeSymbol.translateToObjCParameters(baseMethodBridge: MethodBridge): List<ObjCParameter> {
|
||||
@@ -27,13 +25,19 @@ internal fun KtFunctionLikeSymbol.translateToObjCParameters(baseMethodBridge: Me
|
||||
|
||||
val usedNames = mutableSetOf<String>()
|
||||
|
||||
valueParametersAssociated.forEach { (bridge: MethodBridgeValueParameter, parameter: KtValueParameterSymbol?) ->
|
||||
val candidateName: String = when (bridge) {
|
||||
valueParametersAssociated.forEach { (bridge: MethodBridgeValueParameter, parameter: KtObjCParameterData?) ->
|
||||
val candidateName = when (bridge) {
|
||||
is MethodBridgeValueParameter.Mapped -> {
|
||||
if (parameter == null) throw IllegalStateException("Parameter shouldn't be null")
|
||||
if (parameter == null) return@forEach
|
||||
when {
|
||||
this is KtPropertySetterSymbol -> "value"
|
||||
else -> parameter.name.toString()
|
||||
this is KtPropertySetterSymbol -> {
|
||||
if (parameter.isReceiver) "receiver"
|
||||
else "value"
|
||||
}
|
||||
else -> {
|
||||
if (parameter.isReceiver) "receiver"
|
||||
else parameter.name.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBridgeValueParameter.ErrorOutParameter -> "error"
|
||||
@@ -45,10 +49,10 @@ internal fun KtFunctionLikeSymbol.translateToObjCParameters(baseMethodBridge: Me
|
||||
|
||||
val type = when (bridge) {
|
||||
is MethodBridgeValueParameter.Mapped -> {
|
||||
val returnType = parameter!!.returnType
|
||||
val returnType = parameter!!.type
|
||||
if (parameter.isVararg) {
|
||||
//vararg is a special case, [parameter.returnType] is T, we need Array<T>
|
||||
buildClassType(StandardClassIds.Array) { argument(parameter.returnType) }.translateToObjCType(bridge.bridge)
|
||||
buildClassType(StandardClassIds.Array) { argument(parameter.type) }.translateToObjCType(bridge.bridge)
|
||||
} else {
|
||||
returnType.translateToObjCType(bridge.bridge)
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol? {
|
||||
val members = getCallableSymbolsForObjCMemberTranslation()
|
||||
.filter { it.isObjCBaseCallable() }
|
||||
.sortedWith(StableCallableOrder)
|
||||
.mapNotNull { it.translateToObjCExportStub() }
|
||||
.flatMap { it.translateToObjCExportStub() }
|
||||
|
||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ fun KtResolvedObjCExportFile.translateToObjCTopLevelFacade(): ObjCInterface? {
|
||||
origin = null,
|
||||
attributes = listOf(OBJC_SUBCLASSING_RESTRICTED) + fileName.toNameAttributes(),
|
||||
superProtocols = emptyList(),
|
||||
members = extensions.mapNotNull { it.translateToObjCExportStub() },
|
||||
members = extensions.flatMap { it.translateToObjCExportStub() },
|
||||
categoryName = null,
|
||||
generics = emptyList(),
|
||||
superClass = getDefaultSuperClassOrProtocolName().objCName,
|
||||
|
||||
+18
-6
@@ -6,13 +6,12 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
|
||||
|
||||
internal val StableFileOrder: Comparator<KtObjCExportFile>
|
||||
get() = compareBy<KtObjCExportFile> { file -> file.packageFqName.asString() }
|
||||
.thenComparing { file -> file.fileName }
|
||||
|
||||
internal val StablePropertyOrder: Comparator<KtPropertySymbol> = compareBy { it.name }
|
||||
|
||||
internal val StableFunctionOrder: Comparator<KtFunctionSymbol>
|
||||
get() = compareBy(
|
||||
{ it.isConstructor },
|
||||
@@ -42,6 +41,20 @@ internal val StableClassifierOrder: Comparator<KtClassifierSymbol> =
|
||||
else ""
|
||||
}
|
||||
|
||||
//Figure out when name is special and enable logic. See KT-66510
|
||||
// when (it) {
|
||||
// is KtPropertySymbol -> {
|
||||
// /**
|
||||
// * K1 property names are special: [org.jetbrains.kotlin.name.Name.special] == true
|
||||
// * So we need to wrap setter/getter
|
||||
// */
|
||||
// if (it.setter == null) "<get-${it.name}>"
|
||||
// else "<set-${it.name}>"
|
||||
// }
|
||||
// else -> it.name.toString()
|
||||
// }
|
||||
internal val StableNamedOrder: Comparator<KtNamedSymbol> = compareBy { it.name.toString() }
|
||||
|
||||
internal val StableCallableOrder: Comparator<KtCallableSymbol> = compareBy<KtCallableSymbol> {
|
||||
when (it) {
|
||||
is KtConstructorSymbol -> 0
|
||||
@@ -49,12 +62,11 @@ internal val StableCallableOrder: Comparator<KtCallableSymbol> = compareBy<KtCal
|
||||
is KtPropertySymbol -> 2
|
||||
else -> 3
|
||||
}
|
||||
}
|
||||
.thenComparing(StableConstructorOrder)
|
||||
.thenComparing(StablePropertyOrder)
|
||||
}.thenComparing(StableConstructorOrder)
|
||||
.thenComparing(StableFunctionOrder)
|
||||
.thenComparing(StableNamedOrder)
|
||||
|
||||
private inline fun <T, reified R> Comparator<T>.thenComparing(comparator: Comparator<R>): Comparator<T> where R : T {
|
||||
private inline fun <T, reified R> Comparator<T>.thenComparing(comparator: Comparator<R>): Comparator<T> {
|
||||
return thenComparing { a, b ->
|
||||
if (a is R && b is R) comparator.compare(a, b) else 0
|
||||
}
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.MethodBridge
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.MethodBridgeValueParameter
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ReferenceBridge
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
/**
|
||||
* See K1 implementation [org.jetbrains.kotlin.backend.konan.objcexport.MethodBrideExtensionsKt.valueParametersAssociated]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
internal fun MethodBridge.valueParametersAssociated(
|
||||
function: KtFunctionLikeSymbol,
|
||||
): List<Pair<MethodBridgeValueParameter, KtObjCParameterData?>> {
|
||||
|
||||
val result = mutableListOf<Pair<MethodBridgeValueParameter, KtObjCParameterData?>>()
|
||||
val valueParameters = function.valueParameters
|
||||
val receiverType = function.objCReceiverType
|
||||
val receiverTypeName = receiverType?.expandedClassSymbol?.name
|
||||
|
||||
if (receiverType != null && receiverTypeName != null) {
|
||||
result.add(
|
||||
MethodBridgeValueParameter.Mapped(ReferenceBridge) to KtObjCParameterData(
|
||||
name = receiverTypeName,
|
||||
isVararg = false,
|
||||
type = receiverType,
|
||||
isReceiver = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
this.valueParameters.forEachIndexed { index, valueParameterBridge ->
|
||||
|
||||
if (valueParameterBridge is MethodBridgeValueParameter.Mapped) {
|
||||
result.add(
|
||||
valueParameterBridge to KtObjCParameterData(
|
||||
name = valueParameters[index].name,
|
||||
isVararg = valueParameters[index].isVararg,
|
||||
type = valueParameters[index].returnType,
|
||||
isReceiver = false
|
||||
)
|
||||
)
|
||||
} else {
|
||||
result.add(valueParameterBridge to null)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
internal data class KtObjCParameterData(
|
||||
val name: Name,
|
||||
val isVararg: Boolean,
|
||||
val type: KtType,
|
||||
val isReceiver: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Not null in 3 cases:
|
||||
* 1. constructor of inner class
|
||||
* 2. function extension of inner class
|
||||
* 3. property extension of inner class
|
||||
*
|
||||
* Members with non null [objCReceiverType] will have name `receiver`:
|
||||
* ```objective-c
|
||||
* - (int32_t)foo:(NSString *)receiver __attribute__((swift_name("foo(_:)")));
|
||||
* ```
|
||||
*
|
||||
* Also see [isObjCProperty]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
internal val KtFunctionLikeSymbol.objCReceiverType: KtType?
|
||||
get() {
|
||||
return if (isConstructor) {
|
||||
/**
|
||||
* Edge case for supporting inner classes parameter.
|
||||
* See details at KT-66339
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
getDispatchReceiverType()
|
||||
} else if (isExtension) {
|
||||
if ((getContainingSymbol() as? KtNamedClassOrObjectSymbol)?.isInner == true) receiverParameter?.type
|
||||
else null
|
||||
} else if (this is KtPropertyGetterSymbol || this is KtPropertySetterSymbol) {
|
||||
val property = this.getContainingSymbol() as KtPropertySymbol
|
||||
val isExtension = property.isExtension
|
||||
val isInner = (property.getContainingSymbol() as? KtNamedClassOrObjectSymbol)?.isInner == true
|
||||
if (isExtension && isInner) property.receiverType else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.tests
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.objcexport.objCReceiverType
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.getFunctionOrFail
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.getPropertyOrFail
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class ObjCReceiverTypeTest(
|
||||
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
|
||||
) {
|
||||
|
||||
@Test
|
||||
fun `test - inner constructor`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class Outer {
|
||||
inner class Inner
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
|
||||
val outerClass = file.getClassOrFail("Outer")
|
||||
val innerClass = outerClass.getMemberScope().getClassOrFail("Inner")
|
||||
val innerClassConstructor = innerClass.getMemberScope().getConstructors().first()
|
||||
|
||||
assertEquals(
|
||||
innerClassConstructor.objCReceiverType?.expandedClassSymbol?.classIdIfNonLocal,
|
||||
outerClass.classIdIfNonLocal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - inner function`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun String.foo() = Unit
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
|
||||
val outerClass = file.getClassOrFail("Outer")
|
||||
val innerClass = outerClass.getMemberScope().getClassOrFail("Inner")
|
||||
val foo = innerClass.getMemberScope().getFunctionOrFail("foo")
|
||||
|
||||
assertEquals(
|
||||
ClassId.topLevel(StandardNames.FqNames.string.toSafe()),
|
||||
foo.objCReceiverType?.expandedClassSymbol?.classIdIfNonLocal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - inner getter`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
val Boolean.prop: Boolean
|
||||
get() = false
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
|
||||
val outerClass = file.getClassOrFail("Outer")
|
||||
val innerClass = outerClass.getMemberScope().getClassOrFail("Inner")
|
||||
val getter = innerClass.getMemberScope().getPropertyOrFail("prop").getter
|
||||
|
||||
assertEquals(
|
||||
ClassId.topLevel(StandardNames.FqNames._boolean.toSafe()),
|
||||
getter?.objCReceiverType?.expandedClassSymbol?.classIdIfNonLocal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - inner setter`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
var String.prop: Int
|
||||
get() = 42
|
||||
set(value) {}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
|
||||
val outerClass = file.getClassOrFail("Outer")
|
||||
val innerClass = outerClass.getMemberScope().getClassOrFail("Inner")
|
||||
val setter = innerClass.getMemberScope().getPropertyOrFail("prop").setter
|
||||
|
||||
assertEquals(
|
||||
ClassId.topLevel(StandardNames.FqNames.string.toSafe()),
|
||||
setter?.objCReceiverType?.expandedClassSymbol?.classIdIfNonLocal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - regular extensions`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class Foo {
|
||||
|
||||
fun Boolean.foo() = Unit
|
||||
|
||||
var String.prop: Int
|
||||
get() = 42
|
||||
set(value) {}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
analyze(file) {
|
||||
|
||||
val fooClass = file.getClassOrFail("Foo")
|
||||
val foo = fooClass.getMemberScope().getFunctionOrFail("foo")
|
||||
val setter = fooClass.getMemberScope().getPropertyOrFail("prop").setter
|
||||
val getter = fooClass.getMemberScope().getPropertyOrFail("prop").getter
|
||||
|
||||
assertNull(foo.objCReceiverType)
|
||||
assertNull(setter?.objCReceiverType)
|
||||
assertNull(getter?.objCReceiverType)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user