[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.symbols.*
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportSession
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
context(KtAnalysisSession)
|
||||
internal fun KtClassOrObjectSymbol.members(): List<KtSymbol> {
|
||||
return getMemberScope()
|
||||
.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.builtins.StandardNames
|
||||
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.isVisibleInObjC
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.members
|
||||
@@ -19,7 +20,7 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
if (!isVisibleInObjC()) return null
|
||||
|
||||
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 enumKind = this.classKind == KtClassKind.ENUM_CLASS
|
||||
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 {
|
||||
val normalizedName = name
|
||||
.replaceFirstChar(Char::uppercaseChar)
|
||||
@@ -72,4 +71,4 @@ private val mustBeDocumentedAnnotationsStopList = setOf(
|
||||
StandardNames.FqNames.deprecated,
|
||||
KonanFqNames.objCName,
|
||||
KonanFqNames.shouldRefineInSwift
|
||||
)
|
||||
)
|
||||
|
||||
+20
-8
@@ -6,24 +6,23 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
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.KtClassKind.CLASS
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind.INTERFACE
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtScope.translateToObjCHeader(): ObjCHeader {
|
||||
val declarationsInScope = getAllSymbols()
|
||||
.flatMap { symbol -> symbol.translateToObjCExportStubs() }
|
||||
.toList()
|
||||
|
||||
fun translateToObjCHeader(files: List<KtFile>) : ObjCHeader {
|
||||
val declarations = files.flatMap { ktFile -> ktFile.translateToObjCExportStubs() }
|
||||
return ObjCHeader(
|
||||
stubs = declarationsInScope,
|
||||
stubs = declarations,
|
||||
classForwardDeclarations = emptySet(),
|
||||
protocolForwardDeclarations = declarationsInScope
|
||||
protocolForwardDeclarations = declarations
|
||||
.filterIsInstance<ObjCProtocol>()
|
||||
.flatMap { it.superProtocols }
|
||||
.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)
|
||||
internal fun KtSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
|
||||
return when {
|
||||
this is KtFileSymbol -> translateToObjCExportStubs()
|
||||
this is KtClassOrObjectSymbol && classKind == INTERFACE -> listOfNotNull(translateToObjCProtocol())
|
||||
this is KtClassOrObjectSymbol && classKind == CLASS -> listOfNotNull(translateToObjCClass())
|
||||
this is KtConstructorSymbol -> translateToObjCConstructors()
|
||||
|
||||
+38
-276
@@ -2,25 +2,26 @@
|
||||
|
||||
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.annotations.KtAnnotationApplicationWithArgumentsInfo
|
||||
import org.jetbrains.kotlin.analysis.api.annotations.annotationInfos
|
||||
import org.jetbrains.kotlin.analysis.api.annotations.annotations
|
||||
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.backend.konan.InternalKotlinNativeApi
|
||||
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.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.NameUtils
|
||||
import org.jetbrains.kotlin.objcexport.Predefined.anyMethodSelectors
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getMethodBridge
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
internal val KtCallableSymbol.isConstructor: Boolean
|
||||
get() = this is KtConstructorSymbol
|
||||
@@ -29,27 +30,39 @@ internal val KtCallableSymbol.isArray: Boolean
|
||||
get() = false //TODO: temp k2 workaround
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFunctionSymbol.translateToObjCMethod(): ObjCMethod? {
|
||||
fun KtFunctionSymbol.translateToObjCMethod(
|
||||
): ObjCMethod? {
|
||||
if (!isVisibleInObjC()) return null
|
||||
if (anyMethodSelectors.containsKey(this.name)) return null //temp, find replacement for org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.isReal
|
||||
|
||||
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]
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtFunctionLikeSymbol.buildObjCMethod(unavailable: Boolean = false): ObjCMethod {
|
||||
val baseMethodBridge = bridgeMethodImpl()
|
||||
internal fun KtFunctionLikeSymbol.buildObjCMethod(
|
||||
unavailable: Boolean = false,
|
||||
): ObjCMethod {
|
||||
val bridge = getMethodBridge()
|
||||
val origin: ObjCExportStubOrigin? = null
|
||||
val returnType: ObjCType = mapReturnType(baseMethodBridge.returnBridge)
|
||||
val parameters = collectParameters(baseMethodBridge, this)
|
||||
val selector = getSelector()
|
||||
val returnType: ObjCType = mapReturnType(bridge.returnBridge)
|
||||
val parameters = translateToObjCParameters(bridge)
|
||||
val selector = getSelector(bridge)
|
||||
val selectors: List<String> = splitSelector(selector)
|
||||
val swiftName = getSwiftName()
|
||||
val swiftName = getSwiftName(bridge)
|
||||
val attributes = mutableListOf<String>()
|
||||
val returnBridge = baseMethodBridge.returnBridge
|
||||
val comment = buildComment(this, baseMethodBridge, parameters)
|
||||
val returnBridge = bridge.returnBridge
|
||||
val comment = buildComment(this, bridge, parameters)
|
||||
|
||||
attributes += getSwiftPrivateAttribute() ?: swiftNameAttribute(swiftName)
|
||||
|
||||
@@ -74,78 +87,16 @@ internal fun KtFunctionLikeSymbol.buildObjCMethod(unavailable: Boolean = false):
|
||||
}
|
||||
|
||||
return ObjCMethod(
|
||||
comment,
|
||||
origin,
|
||||
true,
|
||||
returnType,
|
||||
selectors,
|
||||
parameters,
|
||||
attributes
|
||||
comment = comment,
|
||||
origin = origin,
|
||||
isInstanceMethod = bridge.isInstance || isConstructor,
|
||||
returnType = returnType,
|
||||
selectors = selectors,
|
||||
parameters = parameters,
|
||||
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]
|
||||
@@ -269,12 +220,12 @@ internal fun KtCallableSymbol.isRefinedInSwift(): Boolean = when {
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtFunctionLikeSymbol.getSwiftName(): String {
|
||||
internal fun KtFunctionLikeSymbol.getSwiftName(methodBridge: MethodBridge): String {
|
||||
|
||||
//assert(mapper.isBaseMethod(method)) //TODO: implement isBaseMethod
|
||||
getPredefined(this, Predefined.anyMethodSwiftNames)?.let { return it }
|
||||
|
||||
val parameters = bridgeMethodImpl().valueParametersAssociated(this)
|
||||
val parameters = methodBridge.valueParametersAssociated(this)
|
||||
val method = this
|
||||
|
||||
val sb = StringBuilder().apply {
|
||||
@@ -343,11 +294,11 @@ private fun <T : Any> getPredefined(method: KtFunctionLikeSymbol, predefinedForA
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getSelector]
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFunctionLikeSymbol.getSelector(): String {
|
||||
fun KtFunctionLikeSymbol.getSelector(methodBridge: MethodBridge): String {
|
||||
|
||||
getPredefined(this, Predefined.anyMethodSelectors)?.let { return it }
|
||||
|
||||
val parameters = bridgeMethodImpl().valueParametersAssociated(this)
|
||||
val parameters = methodBridge.valueParametersAssociated(this)
|
||||
|
||||
val method = this
|
||||
|
||||
@@ -389,35 +340,6 @@ fun KtFunctionLikeSymbol.getSelector(): String {
|
||||
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)
|
||||
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]
|
||||
*/
|
||||
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
|
||||
fun MethodBridge.valueParametersAssociated(
|
||||
@@ -513,7 +435,7 @@ fun KtFunctionLikeSymbol.mapReturnType(returnBridge: MethodBridge.ReturnValue):
|
||||
if (!returnBridge.successMayBeZero) {
|
||||
check(
|
||||
successReturnType is ObjCNonNullReferenceType
|
||||
|| (successReturnType is ObjCPointerType && !successReturnType.nullable)
|
||||
|| (successReturnType is ObjCPointerType && !successReturnType.nullable)
|
||||
) {
|
||||
"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]
|
||||
@@ -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.backend.konan.objcexport.ObjCProperty
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.swiftNameAttribute
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getMethodBridge
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
@@ -24,12 +25,12 @@ context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtPropertySymbol.buildProperty(): ObjCProperty {
|
||||
val propertyName = getObjCPropertyName()
|
||||
val name = propertyName.objCName
|
||||
val getterBridge = getter?.bridgeMethodImpl()
|
||||
val getterBridge = getter?.getMethodBridge()
|
||||
val type = getter?.mapReturnType(getterBridge!!.returnBridge)
|
||||
val attributes = mutableListOf<String>()
|
||||
val setterName: String?
|
||||
|
||||
if (!getterBridge!!.isInstance) {
|
||||
if (getterBridge!!.isInstance) {
|
||||
attributes += "class"
|
||||
}
|
||||
|
||||
@@ -38,7 +39,7 @@ fun KtPropertySymbol.buildProperty(): ObjCProperty {
|
||||
val shouldBeSetterExposed = true //TODO: mapper.shouldBeExposed
|
||||
|
||||
if (propertySetter != null && shouldBeSetterExposed) {
|
||||
val setterSelector = propertySetter.getSelector()
|
||||
val setterSelector = propertySetter.getSelector(getterBridge)
|
||||
setterName = if (setterSelector != "set" + name.replaceFirstChar(Char::uppercaseChar) + ":") setterSelector else null
|
||||
} else {
|
||||
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.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.KtNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -43,3 +44,7 @@ fun KtScope.getFunctionOrFail(name: String): KtFunctionSymbol {
|
||||
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 (module, files) = session.modulesWithFiles.entries.single()
|
||||
return analyze(module) {
|
||||
val scope = files.map { it as KtFile }.map { it.getFileSymbol().getFileScope() }.asCompositeScope()
|
||||
|
||||
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 ObjCExportFunctionName: ObjCExportName
|
||||
interface ObjCExportFunctionName : ObjCExportName
|
||||
|
||||
interface ObjCExportFileName : ObjCExportName
|
||||
|
||||
fun ObjCExportClassOrProtocolName(
|
||||
swiftName: String,
|
||||
@@ -34,7 +36,6 @@ private data class ObjCExportClassOrProtocolNameImpl(
|
||||
override val swiftName: String,
|
||||
override val objCName: String,
|
||||
override val binaryName: String,
|
||||
|
||||
) : ObjCExportClassOrProtocolName
|
||||
|
||||
fun ObjCExportPropertyName(
|
||||
@@ -53,6 +54,14 @@ fun ObjCExportFunctionName(
|
||||
objCName = objCName
|
||||
)
|
||||
|
||||
fun ObjCExportFileName(
|
||||
swiftName: String,
|
||||
objCName: String,
|
||||
): ObjCExportFileName = ObjCExportFileNameImpl(
|
||||
swiftName = swiftName,
|
||||
objCName = objCName
|
||||
)
|
||||
|
||||
private data class ObjCExportPropertyNameImpl(
|
||||
override val swiftName: String,
|
||||
override val objCName: String,
|
||||
@@ -63,6 +72,11 @@ private data class ObjCExportFunctionNameImpl(
|
||||
override val objCName: String,
|
||||
) : ObjCExportFunctionName
|
||||
|
||||
private data class ObjCExportFileNameImpl(
|
||||
override val swiftName: String,
|
||||
override val objCName: String,
|
||||
) : ObjCExportFileName
|
||||
|
||||
|
||||
fun ObjCExportClassOrProtocolName.toNameAttributes(): List<String> = listOfNotNull(
|
||||
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
|
||||
|
||||
private fun String.toIdentifier(): String = this.toValidObjCSwiftIdentifier()
|
||||
@InternalKotlinNativeApi
|
||||
fun String.toIdentifier(): String = this.toValidObjCSwiftIdentifier()
|
||||
|
||||
internal fun String.toValidObjCSwiftIdentifier(): String {
|
||||
if (this.isEmpty()) return "__"
|
||||
|
||||
Reference in New Issue
Block a user