[ObjCExport] Add initial top level function export
KT-64874
This commit is contained in:
committed by
Space Team
parent
e67042118a
commit
a778c6d9aa
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportClassOrProtocolName
|
||||||
|
import org.jetbrains.kotlin.objcexport.KtObjCExportSession
|
||||||
|
import org.jetbrains.kotlin.objcexport.getObjCKotlinStdlibClassOrProtocolName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some entities like top level functions are wrapped into classes with Base super class.
|
||||||
|
*
|
||||||
|
* @interface FooKt : Base
|
||||||
|
* + (NSString *)myTopLevelFunction __attribute__((swift_name("myTopLevelFunction()")));
|
||||||
|
* @end
|
||||||
|
*/
|
||||||
|
context(KtObjCExportSession)
|
||||||
|
internal fun getDefaultSuperClassOrProtocolName(): ObjCExportClassOrProtocolName {
|
||||||
|
return "Base".getObjCKotlinStdlibClassOrProtocolName()
|
||||||
|
}
|
||||||
+172
@@ -0,0 +1,172 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.types.KtFunctionalType
|
||||||
|
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||||
|
import org.jetbrains.kotlin.backend.konan.KonanPrimitiveType
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||||
|
import org.jetbrains.kotlin.objcexport.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.bridgeMethodImpl]
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
internal fun KtFunctionLikeSymbol.getMethodBridge(): MethodBridge {
|
||||||
|
|
||||||
|
val valueParameters = mutableListOf<MethodBridgeValueParameter>()
|
||||||
|
|
||||||
|
val receiver = if (isConstructor && isArray) {
|
||||||
|
MethodBridgeReceiver.Factory
|
||||||
|
} else if (isTopLevel) {
|
||||||
|
MethodBridgeReceiver.Static
|
||||||
|
} else {
|
||||||
|
MethodBridgeReceiver.Instance
|
||||||
|
}
|
||||||
|
|
||||||
|
this.valueParameters.forEach {
|
||||||
|
valueParameters += bridgeParameter(it.returnType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this is KtFunctionSymbol && isSuspend) {
|
||||||
|
valueParameters += MethodBridgeValueParameter.SuspendCompletion(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return MethodBridge(
|
||||||
|
bridgeReturnType(),
|
||||||
|
receiver,
|
||||||
|
valueParameters
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ObjCExportMapper.bridgeParameter]
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
private fun bridgeParameter(type: KtType): MethodBridgeValueParameter {
|
||||||
|
return MethodBridgeValueParameter.Mapped(bridgeType(type))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ObjCExportMapper.bridgeType]
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
private fun bridgeType(
|
||||||
|
type: KtType,
|
||||||
|
): TypeBridge {
|
||||||
|
|
||||||
|
return if (type.isPrimitive) {
|
||||||
|
val objCType = when {
|
||||||
|
type.isBoolean -> ObjCValueType.BOOL
|
||||||
|
type.isChar -> ObjCValueType.UNICHAR
|
||||||
|
type.isByte -> ObjCValueType.CHAR
|
||||||
|
type.isShort -> ObjCValueType.SHORT
|
||||||
|
type.isInt -> ObjCValueType.INT
|
||||||
|
type.isLong -> ObjCValueType.LONG_LONG
|
||||||
|
type.isFloat -> ObjCValueType.FLOAT
|
||||||
|
type.isDouble -> ObjCValueType.DOUBLE
|
||||||
|
type.isUByte -> ObjCValueType.UNSIGNED_CHAR
|
||||||
|
type.isUShort -> ObjCValueType.UNSIGNED_SHORT
|
||||||
|
type.isUInt -> ObjCValueType.UNSIGNED_INT
|
||||||
|
type.isULong -> ObjCValueType.UNSIGNED_LONG_LONG
|
||||||
|
else ->
|
||||||
|
/**
|
||||||
|
* Handle [KonanPrimitiveType.NON_NULL_NATIVE_PTR] and [KonanPrimitiveType.VECTOR128]
|
||||||
|
*/
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
ValueTypeBridge(objCType)
|
||||||
|
} else if (type.isFunctionType) {
|
||||||
|
bridgeFunctionType(type)
|
||||||
|
} else {
|
||||||
|
ReferenceBridge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ObjCExportMapper.bridgeFunctionType]
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
private fun bridgeFunctionType(type: KtType): TypeBridge {
|
||||||
|
|
||||||
|
val numberOfParameters: Int
|
||||||
|
val returnType: KtType
|
||||||
|
|
||||||
|
if (type is KtFunctionalType) {
|
||||||
|
numberOfParameters = type.parameterTypes.size
|
||||||
|
returnType = type.parameterTypes.last()
|
||||||
|
} else {
|
||||||
|
numberOfParameters = 0
|
||||||
|
returnType = type
|
||||||
|
}
|
||||||
|
|
||||||
|
val returnsVoid = returnType.isUnit || returnType.isNothing
|
||||||
|
return BlockPointerBridge(numberOfParameters, returnsVoid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ObjCExportMapper.bridgeReturnType]
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
private fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
|
||||||
|
|
||||||
|
val convertExceptionsToErrors = false // TODO: Add exception handling and return MethodBridge.ReturnValue.WithError.ZeroForError
|
||||||
|
|
||||||
|
if (isArray) {
|
||||||
|
return MethodBridge.ReturnValue.Instance.FactoryResult
|
||||||
|
} else if (isConstructor) {
|
||||||
|
val result = MethodBridge.ReturnValue.Instance.InitResult
|
||||||
|
if (convertExceptionsToErrors) {
|
||||||
|
MethodBridge.ReturnValue.WithError.ZeroForError(result, successMayBeZero = false)
|
||||||
|
} else {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
} else if (returnType.isSuspendFunctionType) {
|
||||||
|
return MethodBridge.ReturnValue.Suspend
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: handle hashCode
|
||||||
|
// descriptor.containingDeclaration.let { it is ClassDescriptor && KotlinBuiltIns.isAny(it) } &&
|
||||||
|
// descriptor.name.asString() == "hashCode" -> {
|
||||||
|
// assert(!convertExceptionsToErrors)
|
||||||
|
// MethodBridge.ReturnValue.HashCode
|
||||||
|
// }
|
||||||
|
|
||||||
|
//TODO: handle getter
|
||||||
|
// descriptor is PropertyGetterDescriptor -> {
|
||||||
|
// assert(!convertExceptionsToErrors)
|
||||||
|
// MethodBridge.ReturnValue.Mapped(bridgePropertyType(descriptor.correspondingProperty))
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (returnType.isUnit || returnType.isNothing) {
|
||||||
|
return if (convertExceptionsToErrors) {
|
||||||
|
MethodBridge.ReturnValue.WithError.Success
|
||||||
|
} else {
|
||||||
|
MethodBridge.ReturnValue.Void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val returnTypeBridge = bridgeType(returnType)
|
||||||
|
val successReturnValueBridge = MethodBridge.ReturnValue.Mapped(returnTypeBridge)
|
||||||
|
|
||||||
|
return if (convertExceptionsToErrors) {
|
||||||
|
val canReturnZero = !returnTypeBridge.isReferenceOrPointer() || returnType.canBeNull
|
||||||
|
MethodBridge.ReturnValue.WithError.ZeroForError(
|
||||||
|
successReturnValueBridge,
|
||||||
|
successMayBeZero = canReturnZero
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
successReturnValueBridge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.isReferenceOrPointer]
|
||||||
|
*/
|
||||||
|
private fun TypeBridge.isReferenceOrPointer(): Boolean = when (this) {
|
||||||
|
ReferenceBridge, is BlockPointerBridge -> true
|
||||||
|
is ValueTypeBridge -> this.objCValueType == ObjCValueType.POINTER
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||||
|
|
||||||
|
internal val KtCallableSymbol.isTopLevel: Boolean
|
||||||
|
get() = callableIdIfNonLocal?.classId == null
|
||||||
+1
-2
@@ -2,9 +2,8 @@ package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||||
import org.jetbrains.kotlin.objcexport.KtObjCExportSession
|
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession)
|
||||||
internal fun KtClassOrObjectSymbol.members(): List<KtSymbol> {
|
internal fun KtClassOrObjectSymbol.members(): List<KtSymbol> {
|
||||||
return getMemberScope()
|
return getMemberScope()
|
||||||
.getAllSymbols()
|
.getAllSymbols()
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
|
internal const val OBJC_SUBCLASSING_RESTRICTED = "objc_subclassing_restricted"
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportFileName
|
||||||
|
|
||||||
|
|
||||||
|
context(KtObjCExportSession)
|
||||||
|
internal fun String.getObjCFileName(): ObjCExportFileName {
|
||||||
|
return ObjCExportFileName(
|
||||||
|
swiftName = this,
|
||||||
|
objCName = "${configuration.frameworkName.orEmpty()}$this"
|
||||||
|
)
|
||||||
|
}
|
||||||
+3
-4
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
|||||||
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.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.members
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.members
|
||||||
@@ -19,7 +20,7 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
|||||||
if (!isVisibleInObjC()) return null
|
if (!isVisibleInObjC()) return null
|
||||||
|
|
||||||
val superClass = getSuperClassSymbolNotAny()
|
val superClass = getSuperClassSymbolNotAny()
|
||||||
val kotlinAnyName = "Base".getObjCKotlinStdlibClassOrProtocolName()
|
val kotlinAnyName = getDefaultSuperClassOrProtocolName()
|
||||||
val superName = if (superClass == null) kotlinAnyName else throw RuntimeException("Super class translation isn't implemented yet")
|
val superName = if (superClass == null) kotlinAnyName else throw RuntimeException("Super class translation isn't implemented yet")
|
||||||
val enumKind = this.classKind == KtClassKind.ENUM_CLASS
|
val enumKind = this.classKind == KtClassKind.ENUM_CLASS
|
||||||
val final = if (this is KtSymbolWithModality) this.modality == Modality.FINAL else false
|
val final = if (this is KtSymbolWithModality) this.modality == Modality.FINAL else false
|
||||||
@@ -48,8 +49,6 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val OBJC_SUBCLASSING_RESTRICTED = "objc_subclassing_restricted"
|
|
||||||
|
|
||||||
private fun abbreviate(name: String): String {
|
private fun abbreviate(name: String): String {
|
||||||
val normalizedName = name
|
val normalizedName = name
|
||||||
.replaceFirstChar(Char::uppercaseChar)
|
.replaceFirstChar(Char::uppercaseChar)
|
||||||
@@ -72,4 +71,4 @@ private val mustBeDocumentedAnnotationsStopList = setOf(
|
|||||||
StandardNames.FqNames.deprecated,
|
StandardNames.FqNames.deprecated,
|
||||||
KonanFqNames.objCName,
|
KonanFqNames.objCName,
|
||||||
KonanFqNames.shouldRefineInSwift
|
KonanFqNames.shouldRefineInSwift
|
||||||
)
|
)
|
||||||
|
|||||||
+20
-8
@@ -6,24 +6,23 @@
|
|||||||
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.scopes.KtScope
|
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind.CLASS
|
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind.CLASS
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind.INTERFACE
|
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind.INTERFACE
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
fun KtScope.translateToObjCHeader(): ObjCHeader {
|
fun translateToObjCHeader(files: List<KtFile>) : ObjCHeader {
|
||||||
val declarationsInScope = getAllSymbols()
|
val declarations = files.flatMap { ktFile -> ktFile.translateToObjCExportStubs() }
|
||||||
.flatMap { symbol -> symbol.translateToObjCExportStubs() }
|
|
||||||
.toList()
|
|
||||||
|
|
||||||
return ObjCHeader(
|
return ObjCHeader(
|
||||||
stubs = declarationsInScope,
|
stubs = declarations,
|
||||||
classForwardDeclarations = emptySet(),
|
classForwardDeclarations = emptySet(),
|
||||||
protocolForwardDeclarations = declarationsInScope
|
protocolForwardDeclarations = declarations
|
||||||
.filterIsInstance<ObjCProtocol>()
|
.filterIsInstance<ObjCProtocol>()
|
||||||
.flatMap { it.superProtocols }
|
.flatMap { it.superProtocols }
|
||||||
.toSet(),
|
.toSet(),
|
||||||
@@ -32,9 +31,22 @@ fun KtScope.translateToObjCHeader(): ObjCHeader {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
fun KtFile.translateToObjCExportStubs(): List<ObjCExportStub> {
|
||||||
|
return this.getFileSymbol().translateToObjCExportStubs()
|
||||||
|
}
|
||||||
|
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
fun KtFileSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
|
||||||
|
return listOfNotNull(translateToObjCTopLevelInterfaceFileFacade()) + getFileScope().getClassifierSymbols()
|
||||||
|
.flatMap { classifierSymbol -> classifierSymbol.translateToObjCExportStubs() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
internal fun KtSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
|
internal fun KtSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
|
||||||
return when {
|
return when {
|
||||||
|
this is KtFileSymbol -> translateToObjCExportStubs()
|
||||||
this is KtClassOrObjectSymbol && classKind == INTERFACE -> listOfNotNull(translateToObjCProtocol())
|
this is KtClassOrObjectSymbol && classKind == INTERFACE -> listOfNotNull(translateToObjCProtocol())
|
||||||
this is KtClassOrObjectSymbol && classKind == CLASS -> listOfNotNull(translateToObjCClass())
|
this is KtClassOrObjectSymbol && classKind == CLASS -> listOfNotNull(translateToObjCClass())
|
||||||
this is KtConstructorSymbol -> translateToObjCConstructors()
|
this is KtConstructorSymbol -> translateToObjCConstructors()
|
||||||
|
|||||||
+38
-276
@@ -2,25 +2,26 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.objcexport
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo
|
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo
|
||||||
import org.jetbrains.kotlin.analysis.api.annotations.annotationInfos
|
import org.jetbrains.kotlin.analysis.api.annotations.annotationInfos
|
||||||
import org.jetbrains.kotlin.analysis.api.annotations.annotations
|
import org.jetbrains.kotlin.analysis.api.annotations.annotations
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||||
import org.jetbrains.kotlin.analysis.api.types.KtFunctionalType
|
|
||||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||||
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanPrimitiveType
|
|
||||||
import org.jetbrains.kotlin.backend.konan.cKeywords
|
|
||||||
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.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.descriptors.Visibility
|
import org.jetbrains.kotlin.descriptors.Visibility
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.NameUtils
|
||||||
import org.jetbrains.kotlin.objcexport.Predefined.anyMethodSelectors
|
import org.jetbrains.kotlin.objcexport.Predefined.anyMethodSelectors
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getMethodBridge
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
internal val KtCallableSymbol.isConstructor: Boolean
|
internal val KtCallableSymbol.isConstructor: Boolean
|
||||||
get() = this is KtConstructorSymbol
|
get() = this is KtConstructorSymbol
|
||||||
@@ -29,27 +30,39 @@ internal val KtCallableSymbol.isArray: Boolean
|
|||||||
get() = false //TODO: temp k2 workaround
|
get() = false //TODO: temp k2 workaround
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
fun KtFunctionSymbol.translateToObjCMethod(): ObjCMethod? {
|
fun KtFunctionSymbol.translateToObjCMethod(
|
||||||
|
): ObjCMethod? {
|
||||||
if (!isVisibleInObjC()) return null
|
if (!isVisibleInObjC()) return null
|
||||||
if (anyMethodSelectors.containsKey(this.name)) return null //temp, find replacement for org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.isReal
|
if (anyMethodSelectors.containsKey(this.name)) return null //temp, find replacement for org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.isReal
|
||||||
|
|
||||||
return buildObjCMethod()
|
return buildObjCMethod()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
fun KtFileSymbol.getObjCFileClassOrProtocolName(): ObjCExportFileName? {
|
||||||
|
val ktFile = this.psi as? KtFile ?: return null
|
||||||
|
val name = NameUtils.getPackagePartClassNamePrefix(FileUtil.getNameWithoutExtension(ktFile.name)) + "Kt"
|
||||||
|
return name.toIdentifier().getObjCFileName()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.buildMethod]
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.buildMethod]
|
||||||
*/
|
*/
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
internal fun KtFunctionLikeSymbol.buildObjCMethod(unavailable: Boolean = false): ObjCMethod {
|
internal fun KtFunctionLikeSymbol.buildObjCMethod(
|
||||||
val baseMethodBridge = bridgeMethodImpl()
|
unavailable: Boolean = false,
|
||||||
|
): ObjCMethod {
|
||||||
|
val bridge = getMethodBridge()
|
||||||
val origin: ObjCExportStubOrigin? = null
|
val origin: ObjCExportStubOrigin? = null
|
||||||
val returnType: ObjCType = mapReturnType(baseMethodBridge.returnBridge)
|
val returnType: ObjCType = mapReturnType(bridge.returnBridge)
|
||||||
val parameters = collectParameters(baseMethodBridge, this)
|
val parameters = translateToObjCParameters(bridge)
|
||||||
val selector = getSelector()
|
val selector = getSelector(bridge)
|
||||||
val selectors: List<String> = splitSelector(selector)
|
val selectors: List<String> = splitSelector(selector)
|
||||||
val swiftName = getSwiftName()
|
val swiftName = getSwiftName(bridge)
|
||||||
val attributes = mutableListOf<String>()
|
val attributes = mutableListOf<String>()
|
||||||
val returnBridge = baseMethodBridge.returnBridge
|
val returnBridge = bridge.returnBridge
|
||||||
val comment = buildComment(this, baseMethodBridge, parameters)
|
val comment = buildComment(this, bridge, parameters)
|
||||||
|
|
||||||
attributes += getSwiftPrivateAttribute() ?: swiftNameAttribute(swiftName)
|
attributes += getSwiftPrivateAttribute() ?: swiftNameAttribute(swiftName)
|
||||||
|
|
||||||
@@ -74,78 +87,16 @@ internal fun KtFunctionLikeSymbol.buildObjCMethod(unavailable: Boolean = false):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ObjCMethod(
|
return ObjCMethod(
|
||||||
comment,
|
comment = comment,
|
||||||
origin,
|
origin = origin,
|
||||||
true,
|
isInstanceMethod = bridge.isInstance || isConstructor,
|
||||||
returnType,
|
returnType = returnType,
|
||||||
selectors,
|
selectors = selectors,
|
||||||
parameters,
|
parameters = parameters,
|
||||||
attributes
|
attributes = attributes
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
|
||||||
private fun collectParameters(baseMethodBridge: MethodBridge, method: KtFunctionLikeSymbol): List<ObjCParameter> {
|
|
||||||
fun unifyName(initialName: String, usedNames: Set<String>): String {
|
|
||||||
var unique = initialName.toValidObjCSwiftIdentifier()
|
|
||||||
while (unique in usedNames || unique in cKeywords) {
|
|
||||||
unique += "_"
|
|
||||||
}
|
|
||||||
return unique
|
|
||||||
}
|
|
||||||
|
|
||||||
val valueParametersAssociated = baseMethodBridge.valueParametersAssociated(method)
|
|
||||||
|
|
||||||
val parameters = mutableListOf<ObjCParameter>()
|
|
||||||
|
|
||||||
val usedNames = mutableSetOf<String>()
|
|
||||||
|
|
||||||
valueParametersAssociated.forEach { (bridge: MethodBridgeValueParameter, parameter: KtTypeParameterSymbol?) ->
|
|
||||||
val candidateName: String = when (bridge) {
|
|
||||||
is MethodBridgeValueParameter.Mapped -> {
|
|
||||||
if (parameter == null) throw IllegalStateException("Parameter shouldn't be null")
|
|
||||||
when {
|
|
||||||
method is KtPropertySetterSymbol -> "value"
|
|
||||||
else -> parameter.getObjCName().name(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MethodBridgeValueParameter.ErrorOutParameter -> "error"
|
|
||||||
is MethodBridgeValueParameter.SuspendCompletion -> "completionHandler"
|
|
||||||
}
|
|
||||||
|
|
||||||
val uniqueName = unifyName(candidateName, usedNames)
|
|
||||||
usedNames += uniqueName
|
|
||||||
|
|
||||||
val type = when (bridge) {
|
|
||||||
is MethodBridgeValueParameter.Mapped -> TODO("Fetch KtType from KtTypeParameterSymbol: $parameter")
|
|
||||||
MethodBridgeValueParameter.ErrorOutParameter ->
|
|
||||||
ObjCPointerType(ObjCNullableReferenceType(ObjCClassType("NSError")), nullable = true)
|
|
||||||
|
|
||||||
is MethodBridgeValueParameter.SuspendCompletion -> {
|
|
||||||
val resultType = if (bridge.useUnitCompletion) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
when (val it = method.returnType.translateToObjCReferenceType()) {
|
|
||||||
is ObjCNonNullReferenceType -> ObjCNullableReferenceType(it, isNullableResult = false)
|
|
||||||
is ObjCNullableReferenceType -> ObjCNullableReferenceType(it.nonNullType, isNullableResult = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ObjCBlockPointerType(
|
|
||||||
returnType = ObjCVoidType,
|
|
||||||
parameterTypes = listOfNotNull(
|
|
||||||
resultType,
|
|
||||||
ObjCNullableReferenceType(ObjCClassType("NSError"))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val origin: ObjCExportStubOrigin? = null
|
|
||||||
val todo: Nothing? = null
|
|
||||||
parameters += ObjCParameter(uniqueName, origin, type, todo)
|
|
||||||
}
|
|
||||||
return parameters
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerKt.toValidObjCSwiftIdentifier]
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerKt.toValidObjCSwiftIdentifier]
|
||||||
@@ -269,12 +220,12 @@ internal fun KtCallableSymbol.isRefinedInSwift(): Boolean = when {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
internal fun KtFunctionLikeSymbol.getSwiftName(): String {
|
internal fun KtFunctionLikeSymbol.getSwiftName(methodBridge: MethodBridge): String {
|
||||||
|
|
||||||
//assert(mapper.isBaseMethod(method)) //TODO: implement isBaseMethod
|
//assert(mapper.isBaseMethod(method)) //TODO: implement isBaseMethod
|
||||||
getPredefined(this, Predefined.anyMethodSwiftNames)?.let { return it }
|
getPredefined(this, Predefined.anyMethodSwiftNames)?.let { return it }
|
||||||
|
|
||||||
val parameters = bridgeMethodImpl().valueParametersAssociated(this)
|
val parameters = methodBridge.valueParametersAssociated(this)
|
||||||
val method = this
|
val method = this
|
||||||
|
|
||||||
val sb = StringBuilder().apply {
|
val sb = StringBuilder().apply {
|
||||||
@@ -343,11 +294,11 @@ private fun <T : Any> getPredefined(method: KtFunctionLikeSymbol, predefinedForA
|
|||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getSelector]
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getSelector]
|
||||||
*/
|
*/
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
fun KtFunctionLikeSymbol.getSelector(): String {
|
fun KtFunctionLikeSymbol.getSelector(methodBridge: MethodBridge): String {
|
||||||
|
|
||||||
getPredefined(this, Predefined.anyMethodSelectors)?.let { return it }
|
getPredefined(this, Predefined.anyMethodSelectors)?.let { return it }
|
||||||
|
|
||||||
val parameters = bridgeMethodImpl().valueParametersAssociated(this)
|
val parameters = methodBridge.valueParametersAssociated(this)
|
||||||
|
|
||||||
val method = this
|
val method = this
|
||||||
|
|
||||||
@@ -389,35 +340,6 @@ fun KtFunctionLikeSymbol.getSelector(): String {
|
|||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Not implemented
|
|
||||||
*/
|
|
||||||
private fun KtTypeParameterSymbol.getObjCName(): ObjCExportName {
|
|
||||||
TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
///**
|
|
||||||
// * [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerKt.toIdentifier]
|
|
||||||
// */
|
|
||||||
//private fun String.toIdentifier(): String = this.toValidObjCSwiftIdentifier()
|
|
||||||
|
|
||||||
///**
|
|
||||||
// * [org.jetbrains.kotlin.backend.konan.objcexport.ObjCName]
|
|
||||||
// */
|
|
||||||
//private class ObjCName(
|
|
||||||
// private val kotlinName: String,
|
|
||||||
// private val objCName: String?,
|
|
||||||
// private val swiftName: String?,
|
|
||||||
// val isExact: Boolean,
|
|
||||||
//) {
|
|
||||||
// // TODO: Prevent mangling when objCName or swiftName is provided
|
|
||||||
//
|
|
||||||
// fun asString(forSwift: Boolean): String = swiftName.takeIf { forSwift } ?: objCName ?: kotlinName
|
|
||||||
//
|
|
||||||
// fun asIdentifier(forSwift: Boolean, default: (String) -> String = { it.toIdentifier() }): String =
|
|
||||||
// swiftName.takeIf { forSwift } ?: objCName ?: default(kotlinName)
|
|
||||||
//}
|
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
private fun KtFunctionLikeSymbol.getMangledName(forSwift: Boolean): String {
|
private fun KtFunctionLikeSymbol.getMangledName(forSwift: Boolean): String {
|
||||||
|
|
||||||
@@ -466,7 +388,7 @@ private fun String.mangleIfSpecialFamily(prefix: String): String {
|
|||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.startsWithWords]
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.startsWithWords]
|
||||||
*/
|
*/
|
||||||
private fun String.startsWithWords(words: String) = this.startsWith(words) &&
|
private fun String.startsWithWords(words: String) = this.startsWith(words) &&
|
||||||
(this.length == words.length || !this[words.length].isLowerCase())
|
(this.length == words.length || !this[words.length].isLowerCase())
|
||||||
|
|
||||||
@InternalKotlinNativeApi
|
@InternalKotlinNativeApi
|
||||||
fun MethodBridge.valueParametersAssociated(
|
fun MethodBridge.valueParametersAssociated(
|
||||||
@@ -513,7 +435,7 @@ fun KtFunctionLikeSymbol.mapReturnType(returnBridge: MethodBridge.ReturnValue):
|
|||||||
if (!returnBridge.successMayBeZero) {
|
if (!returnBridge.successMayBeZero) {
|
||||||
check(
|
check(
|
||||||
successReturnType is ObjCNonNullReferenceType
|
successReturnType is ObjCNonNullReferenceType
|
||||||
|| (successReturnType is ObjCPointerType && !successReturnType.nullable)
|
|| (successReturnType is ObjCPointerType && !successReturnType.nullable)
|
||||||
) {
|
) {
|
||||||
"Unexpected return type: $successReturnType in $this"
|
"Unexpected return type: $successReturnType in $this"
|
||||||
}
|
}
|
||||||
@@ -528,44 +450,6 @@ fun KtFunctionLikeSymbol.mapReturnType(returnBridge: MethodBridge.ReturnValue):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.bridgeMethodImpl]
|
|
||||||
*/
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
|
||||||
internal fun KtFunctionLikeSymbol.bridgeMethodImpl(): MethodBridge {
|
|
||||||
val isTopLevel = false //TODO implement and use ObjCExportMapper.isTopLevel
|
|
||||||
val valueParameters = mutableListOf<MethodBridgeValueParameter>()
|
|
||||||
|
|
||||||
val receiver = if (isConstructor && isArray) {
|
|
||||||
MethodBridgeReceiver.Factory
|
|
||||||
} else if (isTopLevel) {
|
|
||||||
MethodBridgeReceiver.Static
|
|
||||||
} else {
|
|
||||||
MethodBridgeReceiver.Instance
|
|
||||||
}
|
|
||||||
|
|
||||||
this.valueParameters.forEach {
|
|
||||||
valueParameters += bridgeParameter(it.returnType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this is KtFunctionSymbol && isSuspend) {
|
|
||||||
valueParameters += MethodBridgeValueParameter.SuspendCompletion(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return MethodBridge(
|
|
||||||
bridgeReturnType(),
|
|
||||||
receiver,
|
|
||||||
valueParameters
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [ObjCExportMapper.bridgeParameter]
|
|
||||||
*/
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
|
||||||
private fun bridgeParameter(type: KtType): MethodBridgeValueParameter {
|
|
||||||
return MethodBridgeValueParameter.Mapped(bridgeType(type))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapType]
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapType]
|
||||||
@@ -599,125 +483,3 @@ private fun KtType.mapType(typeBridge: TypeBridge): ObjCType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.isReferenceOrPointer]
|
|
||||||
*/
|
|
||||||
private fun TypeBridge.isReferenceOrPointer(): Boolean = when (this) {
|
|
||||||
ReferenceBridge, is BlockPointerBridge -> true
|
|
||||||
is ValueTypeBridge -> this.objCValueType == ObjCValueType.POINTER
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [ObjCExportMapper.bridgeFunctionType]
|
|
||||||
*/
|
|
||||||
context(KtAnalysisSession)
|
|
||||||
private fun bridgeFunctionType(type: KtType): TypeBridge {
|
|
||||||
|
|
||||||
val numberOfParameters: Int
|
|
||||||
val returnType: KtType
|
|
||||||
|
|
||||||
if (type is KtFunctionalType) {
|
|
||||||
numberOfParameters = type.parameterTypes.size
|
|
||||||
returnType = type.parameterTypes.last()
|
|
||||||
} else {
|
|
||||||
numberOfParameters = 0
|
|
||||||
returnType = type
|
|
||||||
}
|
|
||||||
|
|
||||||
val returnsVoid = returnType.isUnit || returnType.isNothing
|
|
||||||
return BlockPointerBridge(numberOfParameters, returnsVoid)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [ObjCExportMapper.bridgeType]
|
|
||||||
*/
|
|
||||||
context(KtAnalysisSession)
|
|
||||||
private fun bridgeType(
|
|
||||||
type: KtType,
|
|
||||||
): TypeBridge {
|
|
||||||
|
|
||||||
return if (type.isPrimitive) {
|
|
||||||
val objCType = when {
|
|
||||||
type.isBoolean -> ObjCValueType.BOOL
|
|
||||||
type.isChar -> ObjCValueType.UNICHAR
|
|
||||||
type.isByte -> ObjCValueType.CHAR
|
|
||||||
type.isShort -> ObjCValueType.SHORT
|
|
||||||
type.isInt -> ObjCValueType.INT
|
|
||||||
type.isLong -> ObjCValueType.LONG_LONG
|
|
||||||
type.isFloat -> ObjCValueType.FLOAT
|
|
||||||
type.isDouble -> ObjCValueType.DOUBLE
|
|
||||||
type.isUByte -> ObjCValueType.UNSIGNED_CHAR
|
|
||||||
type.isUShort -> ObjCValueType.UNSIGNED_SHORT
|
|
||||||
type.isUInt -> ObjCValueType.UNSIGNED_INT
|
|
||||||
type.isULong -> ObjCValueType.UNSIGNED_LONG_LONG
|
|
||||||
else ->
|
|
||||||
/**
|
|
||||||
* Handle [KonanPrimitiveType.NON_NULL_NATIVE_PTR] and [KonanPrimitiveType.VECTOR128]
|
|
||||||
*/
|
|
||||||
TODO()
|
|
||||||
}
|
|
||||||
ValueTypeBridge(objCType)
|
|
||||||
} else if (type.isFunctionType) {
|
|
||||||
bridgeFunctionType(type)
|
|
||||||
} else {
|
|
||||||
ReferenceBridge
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [ObjCExportMapper.bridgeReturnType]
|
|
||||||
*/
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
|
||||||
internal fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
|
|
||||||
|
|
||||||
val convertExceptionsToErrors = false // TODO: Add exception handling and return MethodBridge.ReturnValue.WithError.ZeroForError
|
|
||||||
|
|
||||||
if (isArray) {
|
|
||||||
return MethodBridge.ReturnValue.Instance.FactoryResult
|
|
||||||
} else if (isConstructor) {
|
|
||||||
val result = MethodBridge.ReturnValue.Instance.InitResult
|
|
||||||
if (convertExceptionsToErrors) {
|
|
||||||
MethodBridge.ReturnValue.WithError.ZeroForError(result, successMayBeZero = false)
|
|
||||||
} else {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
} else if (returnType.isSuspendFunctionType) {
|
|
||||||
return MethodBridge.ReturnValue.Suspend
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: handle hashCode
|
|
||||||
// descriptor.containingDeclaration.let { it is ClassDescriptor && KotlinBuiltIns.isAny(it) } &&
|
|
||||||
// descriptor.name.asString() == "hashCode" -> {
|
|
||||||
// assert(!convertExceptionsToErrors)
|
|
||||||
// MethodBridge.ReturnValue.HashCode
|
|
||||||
// }
|
|
||||||
|
|
||||||
//TODO: handle getter
|
|
||||||
// descriptor is PropertyGetterDescriptor -> {
|
|
||||||
// assert(!convertExceptionsToErrors)
|
|
||||||
// MethodBridge.ReturnValue.Mapped(bridgePropertyType(descriptor.correspondingProperty))
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (returnType.isUnit || returnType.isNothing) {
|
|
||||||
return if (convertExceptionsToErrors) {
|
|
||||||
MethodBridge.ReturnValue.WithError.Success
|
|
||||||
} else {
|
|
||||||
MethodBridge.ReturnValue.Void
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val returnTypeBridge = bridgeType(returnType)
|
|
||||||
val successReturnValueBridge = MethodBridge.ReturnValue.Mapped(returnTypeBridge)
|
|
||||||
|
|
||||||
return if (convertExceptionsToErrors) {
|
|
||||||
val canReturnZero = !returnTypeBridge.isReferenceOrPointer() || returnType.canBeNull
|
|
||||||
MethodBridge.ReturnValue.WithError.ZeroForError(
|
|
||||||
successReturnValueBridge,
|
|
||||||
successMayBeZero = canReturnZero
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
successReturnValueBridge
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
|
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.backend.konan.cKeywords
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||||
|
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
internal fun KtFunctionLikeSymbol.translateToObjCParameters(baseMethodBridge: MethodBridge): List<ObjCParameter> {
|
||||||
|
fun unifyName(initialName: String, usedNames: Set<String>): String {
|
||||||
|
var unique = initialName.toValidObjCSwiftIdentifier()
|
||||||
|
while (unique in usedNames || unique in cKeywords) {
|
||||||
|
unique += "_"
|
||||||
|
}
|
||||||
|
return unique
|
||||||
|
}
|
||||||
|
|
||||||
|
val valueParametersAssociated = baseMethodBridge.valueParametersAssociated(this)
|
||||||
|
|
||||||
|
val parameters = mutableListOf<ObjCParameter>()
|
||||||
|
|
||||||
|
val usedNames = mutableSetOf<String>()
|
||||||
|
|
||||||
|
valueParametersAssociated.forEach { (bridge: MethodBridgeValueParameter, parameter: KtTypeParameterSymbol?) ->
|
||||||
|
val candidateName: String = when (bridge) {
|
||||||
|
is MethodBridgeValueParameter.Mapped -> {
|
||||||
|
if (parameter == null) throw IllegalStateException("Parameter shouldn't be null")
|
||||||
|
when {
|
||||||
|
this is KtPropertySetterSymbol -> "value"
|
||||||
|
else -> parameter.getObjCName().name(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MethodBridgeValueParameter.ErrorOutParameter -> "error"
|
||||||
|
is MethodBridgeValueParameter.SuspendCompletion -> "completionHandler"
|
||||||
|
}
|
||||||
|
|
||||||
|
val uniqueName = unifyName(candidateName, usedNames)
|
||||||
|
usedNames += uniqueName
|
||||||
|
|
||||||
|
val type = when (bridge) {
|
||||||
|
is MethodBridgeValueParameter.Mapped -> TODO("Fetch KtType from KtTypeParameterSymbol: $parameter")
|
||||||
|
MethodBridgeValueParameter.ErrorOutParameter ->
|
||||||
|
ObjCPointerType(ObjCNullableReferenceType(ObjCClassType("NSError")), nullable = true)
|
||||||
|
|
||||||
|
is MethodBridgeValueParameter.SuspendCompletion -> {
|
||||||
|
val resultType = if (bridge.useUnitCompletion) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
when (val it = this.returnType.translateToObjCReferenceType()) {
|
||||||
|
is ObjCNonNullReferenceType -> ObjCNullableReferenceType(it, isNullableResult = false)
|
||||||
|
is ObjCNullableReferenceType -> ObjCNullableReferenceType(it.nonNullType, isNullableResult = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjCBlockPointerType(
|
||||||
|
returnType = ObjCVoidType,
|
||||||
|
parameterTypes = listOfNotNull(
|
||||||
|
resultType,
|
||||||
|
ObjCNullableReferenceType(ObjCClassType("NSError"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val origin: ObjCExportStubOrigin? = null
|
||||||
|
val todo: Nothing? = null
|
||||||
|
parameters += ObjCParameter(uniqueName, origin, type, todo)
|
||||||
|
}
|
||||||
|
return parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not implemented
|
||||||
|
*/
|
||||||
|
internal fun KtTypeParameterSymbol.getObjCName(): ObjCExportName {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
+4
-3
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
|||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProperty
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProperty
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.swiftNameAttribute
|
import org.jetbrains.kotlin.backend.konan.objcexport.swiftNameAttribute
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getMethodBridge
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
@@ -24,12 +25,12 @@ context(KtAnalysisSession, KtObjCExportSession)
|
|||||||
fun KtPropertySymbol.buildProperty(): ObjCProperty {
|
fun KtPropertySymbol.buildProperty(): ObjCProperty {
|
||||||
val propertyName = getObjCPropertyName()
|
val propertyName = getObjCPropertyName()
|
||||||
val name = propertyName.objCName
|
val name = propertyName.objCName
|
||||||
val getterBridge = getter?.bridgeMethodImpl()
|
val getterBridge = getter?.getMethodBridge()
|
||||||
val type = getter?.mapReturnType(getterBridge!!.returnBridge)
|
val type = getter?.mapReturnType(getterBridge!!.returnBridge)
|
||||||
val attributes = mutableListOf<String>()
|
val attributes = mutableListOf<String>()
|
||||||
val setterName: String?
|
val setterName: String?
|
||||||
|
|
||||||
if (!getterBridge!!.isInstance) {
|
if (getterBridge!!.isInstance) {
|
||||||
attributes += "class"
|
attributes += "class"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ fun KtPropertySymbol.buildProperty(): ObjCProperty {
|
|||||||
val shouldBeSetterExposed = true //TODO: mapper.shouldBeExposed
|
val shouldBeSetterExposed = true //TODO: mapper.shouldBeExposed
|
||||||
|
|
||||||
if (propertySetter != null && shouldBeSetterExposed) {
|
if (propertySetter != null && shouldBeSetterExposed) {
|
||||||
val setterSelector = propertySetter.getSelector()
|
val setterSelector = propertySetter.getSelector(getterBridge)
|
||||||
setterName = if (setterSelector != "set" + name.replaceFirstChar(Char::uppercaseChar) + ":") setterSelector else null
|
setterName = if (setterSelector != "set" + name.replaceFirstChar(Char::uppercaseChar) + ":") setterSelector else null
|
||||||
} else {
|
} else {
|
||||||
attributes += "readonly"
|
attributes += "readonly"
|
||||||
|
|||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInterface
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInterfaceImpl
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates top level functions/properties inside the given [this] file as a single [ObjCInterface].
|
||||||
|
* ## example:
|
||||||
|
* given a file "Foo.kt"
|
||||||
|
*
|
||||||
|
* ```kotlin
|
||||||
|
*
|
||||||
|
* fun myTopLevelFunction() = 42
|
||||||
|
*
|
||||||
|
* val myTopLevelProperty get() = 42
|
||||||
|
*
|
||||||
|
* class Bar {
|
||||||
|
* fun bar() = Unit
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This will be exporting two Interfaces:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* FooKt: Base
|
||||||
|
* - myTopLevelFunction
|
||||||
|
* - myTopLevelProperty
|
||||||
|
*
|
||||||
|
* Bar
|
||||||
|
* - bar
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Where `FooKt` would be the "top level interface file facade" returned by this function.
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
fun KtFileSymbol.translateToObjCTopLevelInterfaceFileFacade(): ObjCInterface? {
|
||||||
|
val topLevelCallableStubs = getFileScope().getCallableSymbols()
|
||||||
|
.flatMap { callableSymbol -> callableSymbol.translateToObjCExportStubs() }
|
||||||
|
.toList()
|
||||||
|
/* If there are no top level functions or properties, we do not need to export a file facade */
|
||||||
|
.ifEmpty { return null }
|
||||||
|
|
||||||
|
val fileName = getObjCFileClassOrProtocolName()
|
||||||
|
?: throw IllegalStateException("Top level file '$this' cannot be translated without file name")
|
||||||
|
|
||||||
|
val name = fileName.objCName
|
||||||
|
val attributes = listOf(OBJC_SUBCLASSING_RESTRICTED)
|
||||||
|
|
||||||
|
val superClass = getDefaultSuperClassOrProtocolName()
|
||||||
|
|
||||||
|
return ObjCInterfaceImpl(
|
||||||
|
name = name,
|
||||||
|
comment = null,
|
||||||
|
origin = null,
|
||||||
|
attributes = attributes,
|
||||||
|
superProtocols = emptyList(),
|
||||||
|
members = topLevelCallableStubs,
|
||||||
|
categoryName = null,
|
||||||
|
generics = emptyList(),
|
||||||
|
superClass = superClass.objCName,
|
||||||
|
superClassGenerics = emptyList()
|
||||||
|
)
|
||||||
|
}
|
||||||
+5
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.objcexport.testUtils
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.scopes.KtScope
|
import org.jetbrains.kotlin.analysis.api.scopes.KtScope
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -43,3 +44,7 @@ fun KtScope.getFunctionOrFail(name: String): KtFunctionSymbol {
|
|||||||
return symbol
|
return symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
fun KtClassOrObjectSymbol.getFunctionOrFail(name: String) : KtFunctionSymbol {
|
||||||
|
return this.getMemberScope().getFunctionOrFail(name)
|
||||||
|
}
|
||||||
|
|||||||
+1
-3
@@ -31,10 +31,8 @@ object AnalysisApiHeaderGenerator : HeaderGenerator {
|
|||||||
val session = createStandaloneAnalysisApiSession(root.listFiles().orEmpty().filter { it.extension == "kt" })
|
val session = createStandaloneAnalysisApiSession(root.listFiles().orEmpty().filter { it.extension == "kt" })
|
||||||
val (module, files) = session.modulesWithFiles.entries.single()
|
val (module, files) = session.modulesWithFiles.entries.single()
|
||||||
return analyze(module) {
|
return analyze(module) {
|
||||||
val scope = files.map { it as KtFile }.map { it.getFileSymbol().getFileScope() }.asCompositeScope()
|
|
||||||
|
|
||||||
KtObjCExportSession(KtObjCExportConfiguration()) {
|
KtObjCExportSession(KtObjCExportConfiguration()) {
|
||||||
scope.translateToObjCHeader().toString()
|
translateToObjCHeader(files.map { it as KtFile }).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport.tests
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.analyze
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isTopLevel
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.getFunctionOrFail
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class IsTopLevelTests(
|
||||||
|
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
|
||||||
|
) {
|
||||||
|
@Test
|
||||||
|
fun `test - top level fun`() {
|
||||||
|
val ktFile = inlineSourceCodeAnalysis.createKtFile(
|
||||||
|
"""
|
||||||
|
fun topFun() {}
|
||||||
|
class TopClass {
|
||||||
|
fun classFun() {}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
analyze(ktFile) {
|
||||||
|
assertTrue(ktFile.getFunctionOrFail("topFun").isTopLevel)
|
||||||
|
assertFalse (ktFile.getClassOrFail("TopClass").getFunctionOrFail("classFun").isTopLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-3
@@ -18,7 +18,9 @@ interface ObjCExportClassOrProtocolName : ObjCExportName {
|
|||||||
|
|
||||||
interface ObjCExportPropertyName : ObjCExportName
|
interface ObjCExportPropertyName : ObjCExportName
|
||||||
|
|
||||||
interface ObjCExportFunctionName: ObjCExportName
|
interface ObjCExportFunctionName : ObjCExportName
|
||||||
|
|
||||||
|
interface ObjCExportFileName : ObjCExportName
|
||||||
|
|
||||||
fun ObjCExportClassOrProtocolName(
|
fun ObjCExportClassOrProtocolName(
|
||||||
swiftName: String,
|
swiftName: String,
|
||||||
@@ -34,7 +36,6 @@ private data class ObjCExportClassOrProtocolNameImpl(
|
|||||||
override val swiftName: String,
|
override val swiftName: String,
|
||||||
override val objCName: String,
|
override val objCName: String,
|
||||||
override val binaryName: String,
|
override val binaryName: String,
|
||||||
|
|
||||||
) : ObjCExportClassOrProtocolName
|
) : ObjCExportClassOrProtocolName
|
||||||
|
|
||||||
fun ObjCExportPropertyName(
|
fun ObjCExportPropertyName(
|
||||||
@@ -53,6 +54,14 @@ fun ObjCExportFunctionName(
|
|||||||
objCName = objCName
|
objCName = objCName
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun ObjCExportFileName(
|
||||||
|
swiftName: String,
|
||||||
|
objCName: String,
|
||||||
|
): ObjCExportFileName = ObjCExportFileNameImpl(
|
||||||
|
swiftName = swiftName,
|
||||||
|
objCName = objCName
|
||||||
|
)
|
||||||
|
|
||||||
private data class ObjCExportPropertyNameImpl(
|
private data class ObjCExportPropertyNameImpl(
|
||||||
override val swiftName: String,
|
override val swiftName: String,
|
||||||
override val objCName: String,
|
override val objCName: String,
|
||||||
@@ -63,6 +72,11 @@ private data class ObjCExportFunctionNameImpl(
|
|||||||
override val objCName: String,
|
override val objCName: String,
|
||||||
) : ObjCExportFunctionName
|
) : ObjCExportFunctionName
|
||||||
|
|
||||||
|
private data class ObjCExportFileNameImpl(
|
||||||
|
override val swiftName: String,
|
||||||
|
override val objCName: String,
|
||||||
|
) : ObjCExportFileName
|
||||||
|
|
||||||
|
|
||||||
fun ObjCExportClassOrProtocolName.toNameAttributes(): List<String> = listOfNotNull(
|
fun ObjCExportClassOrProtocolName.toNameAttributes(): List<String> = listOfNotNull(
|
||||||
binaryName.takeIf { it != objCName }?.let { objcRuntimeNameAttribute(it) },
|
binaryName.takeIf { it != objCName }?.let { objcRuntimeNameAttribute(it) },
|
||||||
@@ -77,7 +91,8 @@ fun objcRuntimeNameAttribute(name: String) = "objc_runtime_name(\"$name\")"
|
|||||||
|
|
||||||
fun ObjCExportName.name(forSwift: Boolean) = swiftName.takeIf { forSwift } ?: objCName
|
fun ObjCExportName.name(forSwift: Boolean) = swiftName.takeIf { forSwift } ?: objCName
|
||||||
|
|
||||||
private fun String.toIdentifier(): String = this.toValidObjCSwiftIdentifier()
|
@InternalKotlinNativeApi
|
||||||
|
fun String.toIdentifier(): String = this.toValidObjCSwiftIdentifier()
|
||||||
|
|
||||||
internal fun String.toValidObjCSwiftIdentifier(): String {
|
internal fun String.toValidObjCSwiftIdentifier(): String {
|
||||||
if (this.isEmpty()) return "__"
|
if (this.isEmpty()) return "__"
|
||||||
|
|||||||
Reference in New Issue
Block a user