[ObjCExport] Add object translation and error handling
- Also add forward declaration tests - Add hidden types support - Issues: KT-64857, KT-64952, KT-65080
This commit is contained in:
committed by
Space Team
parent
c483e54e2d
commit
480b8ec516
+14
@@ -0,0 +1,14 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamer]
|
||||
*/
|
||||
internal object ObjCPropertyNames {
|
||||
@Suppress("unused")
|
||||
const val kotlinThrowableAsErrorMethodName: String = "asError"
|
||||
|
||||
const val objectPropertyName: String = "shared"
|
||||
|
||||
@Suppress("unused")
|
||||
const val companionObjectPropertyName: String = "companion"
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtClassErrorType
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportSession
|
||||
|
||||
/**
|
||||
* Traverses stubs and returns true if [objCErrorType] is used as a return, parameter or property type
|
||||
*/
|
||||
internal fun Iterable<ObjCExportStub>.hasErrorTypes(): Boolean {
|
||||
return any { stub -> stub.hasErrorTypes() }
|
||||
}
|
||||
|
||||
internal fun ObjCExportStub.hasErrorTypes(): Boolean {
|
||||
return when (val stub = this) {
|
||||
is ObjCClass -> stub.members.hasErrorTypes()
|
||||
is ObjCProperty -> stub.type == objCErrorType
|
||||
is ObjCMethod -> {
|
||||
if (stub.returnType == objCErrorType) true
|
||||
else stub.parameters.any { parameter -> parameter.type == objCErrorType }
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
internal val KtType.isError
|
||||
get() = this is KtClassErrorType
|
||||
|
||||
internal const val errorClassName = "ERROR"
|
||||
|
||||
context(KtObjCExportSession)
|
||||
internal val errorInterface
|
||||
get() = ObjCInterfaceImpl(
|
||||
name = errorClassName,
|
||||
comment = null,
|
||||
origin = null,
|
||||
attributes = emptyList(),
|
||||
superProtocols = emptyList(),
|
||||
members = emptyList(),
|
||||
categoryName = null,
|
||||
generics = emptyList(),
|
||||
superClass = getDefaultSuperClassOrProtocolName().objCName,
|
||||
superClassGenerics = emptyList()
|
||||
)
|
||||
|
||||
internal val objCErrorType = ObjCClassType(errorClassName)
|
||||
internal val errorForwardClass = ObjCClassForwardDeclaration(errorClassName)
|
||||
+5
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtClassErrorType
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtClassType
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtClassTypeQualifier
|
||||
|
||||
@@ -29,6 +30,10 @@ context(KtAnalysisSession)
|
||||
internal fun KtClassOrObjectSymbol.getSuperClassSymbolNotAny(): KtClassOrObjectSymbol? {
|
||||
return superTypes.firstNotNullOfOrNull find@{ superType ->
|
||||
if (superType.isAny) return@find null
|
||||
if (superType.isError && superType is KtClassErrorType) {
|
||||
//Header should have just a Base type in case unresolved super type
|
||||
return@find null
|
||||
}
|
||||
if (superType is KtClassType) {
|
||||
val classifier = superType.qualifiers.firstNotNullOfOrNull { qualifier ->
|
||||
(qualifier as? KtClassTypeQualifier.KtResolvedClassTypeQualifier)?.symbol
|
||||
|
||||
+32
-1
@@ -4,7 +4,7 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
|
||||
context(KtAnalysisSession)
|
||||
internal fun KtClassOrObjectSymbol.members(): List<KtSymbol> {
|
||||
internal fun KtClassOrObjectSymbol.getAllMembers(): List<KtSymbol> {
|
||||
return getMemberScope()
|
||||
.getAllSymbols()
|
||||
.sortedBy { sortMembers(it) }
|
||||
@@ -13,6 +13,37 @@ internal fun KtClassOrObjectSymbol.members(): List<KtSymbol> {
|
||||
.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns members explicitly defined in the symbol. All super methods are excluded.
|
||||
*
|
||||
* Also handles edge case with covariant method overwrite:
|
||||
*
|
||||
* ```
|
||||
* interface A {
|
||||
* fun hello(): Any
|
||||
* }
|
||||
*
|
||||
* interface B: A {
|
||||
* override fun hello(): String
|
||||
* }
|
||||
*
|
||||
* B.getBaseMembers().isEmpty() == true
|
||||
* ```
|
||||
*
|
||||
* More context is around [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.isBaseMethod]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
internal fun KtClassOrObjectSymbol.getDeclaredMembers(): List<KtSymbol> {
|
||||
return getDeclaredMemberScope()
|
||||
.getAllSymbols()
|
||||
.sortedBy { sortMembers(it) }
|
||||
.filterIsInstance<KtCallableSymbol>()
|
||||
.filter { member ->
|
||||
member.getAllOverriddenSymbols().isEmpty() && member.isVisibleInObjC()
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Temp workaround of [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorKt.makeMethodsOrderStable]
|
||||
*/
|
||||
|
||||
+2
-2
@@ -6,10 +6,10 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllMembers
|
||||
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
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
@@ -27,7 +27,7 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||
val origin: ObjCExportStubOrigin = getObjCExportStubOrigin()
|
||||
val superProtocols: List<String> = superProtocols()
|
||||
val members: List<ObjCExportStub> = members().flatMap { it.translateToObjCExportStubs() }
|
||||
val members: List<ObjCExportStub> = getAllMembers().flatMap { it.translateToObjCExportStubs() }
|
||||
val categoryName: String? = null
|
||||
val generics: List<ObjCGenericTypeDeclaration> = emptyList()
|
||||
val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList()
|
||||
|
||||
+50
-14
@@ -7,30 +7,65 @@ package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.analysis.api.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.analysis.api.symbols.KtClassKind.*
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.errorForwardClass
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.errorInterface
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasErrorTypes
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun translateToObjCHeader(files: List<KtFile>) : ObjCHeader {
|
||||
val declarations = files.flatMap { ktFile -> ktFile.translateToObjCExportStubs() }
|
||||
fun translateToObjCHeader(files: List<KtFile>): ObjCHeader {
|
||||
val declarations = files.flatMap { ktFile -> ktFile.translateToObjCExportStubs() }.toMutableList()
|
||||
val classForwardDeclarations = getClassForwardDeclarations(declarations).toMutableSet()
|
||||
val protocolForwardDeclarations = getProtocolForwardDeclarations(declarations)
|
||||
|
||||
if (declarations.hasErrorTypes()) {
|
||||
declarations.add(errorInterface)
|
||||
classForwardDeclarations.add(errorForwardClass)
|
||||
}
|
||||
|
||||
return ObjCHeader(
|
||||
stubs = declarations,
|
||||
classForwardDeclarations = emptySet(),
|
||||
protocolForwardDeclarations = declarations
|
||||
.filterIsInstance<ObjCProtocol>()
|
||||
.flatMap { it.superProtocols }
|
||||
.toSet(),
|
||||
classForwardDeclarations = classForwardDeclarations,
|
||||
protocolForwardDeclarations = protocolForwardDeclarations,
|
||||
additionalImports = emptyList(),
|
||||
exportKDoc = configuration.exportKDoc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Class which have static property must have forward declaration
|
||||
*
|
||||
* ```
|
||||
* @class Foo;
|
||||
*
|
||||
* @interface Foo
|
||||
* @property (class) Foo
|
||||
* @end
|
||||
* ```
|
||||
*/
|
||||
private fun getClassForwardDeclarations(declarations: List<ObjCExportStub>): Set<ObjCClassForwardDeclaration> {
|
||||
return declarations
|
||||
.filterIsInstance<ObjCClass>()
|
||||
.filter { clazz ->
|
||||
clazz.members
|
||||
.filterIsInstance<ObjCProperty>()
|
||||
.any { property ->
|
||||
val className = (property.type as? ObjCClassType)?.className == clazz.name
|
||||
val static = property.propertyAttributes.contains("class")
|
||||
className && static
|
||||
}
|
||||
}.map { clazz ->
|
||||
ObjCClassForwardDeclaration(clazz.name)
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
private fun getProtocolForwardDeclarations(declarations: List<ObjCExportStub>) = declarations
|
||||
.filterIsInstance<ObjCClass>()
|
||||
.flatMap { it.superProtocols }
|
||||
.toSet()
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFile.translateToObjCExportStubs(): List<ObjCExportStub> {
|
||||
return this.getFileSymbol().translateToObjCExportStubs()
|
||||
@@ -49,6 +84,7 @@ internal fun KtSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
|
||||
this is KtFileSymbol -> translateToObjCExportStubs()
|
||||
this is KtClassOrObjectSymbol && classKind == INTERFACE -> listOfNotNull(translateToObjCProtocol())
|
||||
this is KtClassOrObjectSymbol && classKind == CLASS -> listOfNotNull(translateToObjCClass())
|
||||
this is KtClassOrObjectSymbol && classKind == OBJECT -> listOfNotNull(translateToObjCObject())
|
||||
this is KtConstructorSymbol -> translateToObjCConstructors()
|
||||
this is KtPropertySymbol -> listOfNotNull(translateToObjCProperty())
|
||||
this is KtFunctionSymbol -> listOfNotNull(translateToObjCMethod())
|
||||
|
||||
+16
-11
@@ -134,7 +134,7 @@ internal fun KtFunctionLikeSymbol.getSwiftName(methodBridge: MethodBridge): Stri
|
||||
1 -> "_"
|
||||
else -> "value"
|
||||
}
|
||||
else -> symbol!!.getObjCName().name(true)
|
||||
else -> symbol!!.name
|
||||
}
|
||||
MethodBridgeValueParameter.ErrorOutParameter -> continue@parameters
|
||||
is MethodBridgeValueParameter.SuspendCompletion -> "completionHandler"
|
||||
@@ -208,7 +208,9 @@ fun KtFunctionLikeSymbol.getSelector(methodBridge: MethodBridge): String {
|
||||
1 -> ""
|
||||
else -> "value"
|
||||
}
|
||||
else -> typeParameterSymbol!!.getObjCName().name(false)
|
||||
else -> {
|
||||
typeParameterSymbol!!.name.toString()
|
||||
}
|
||||
}
|
||||
MethodBridgeValueParameter.ErrorOutParameter -> "error"
|
||||
is MethodBridgeValueParameter.SuspendCompletion -> "completionHandler"
|
||||
@@ -282,32 +284,35 @@ 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())
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.MethodBrideExtensionsKt.valueParametersAssociated]
|
||||
*/
|
||||
@InternalKotlinNativeApi
|
||||
fun MethodBridge.valueParametersAssociated(
|
||||
function: KtFunctionLikeSymbol,
|
||||
): List<Pair<MethodBridgeValueParameter, KtTypeParameterSymbol?>> {
|
||||
): List<Pair<MethodBridgeValueParameter, KtValueParameterSymbol?>> {
|
||||
|
||||
val kotlinParameters = function.typeParameters.iterator()
|
||||
if (!kotlinParameters.hasNext()) return emptyList()
|
||||
val allParameters = function.valueParameters.iterator()
|
||||
if (!allParameters.hasNext()) return emptyList()
|
||||
|
||||
val skipFirstKotlinParameter = when (this.receiver) {
|
||||
MethodBridgeReceiver.Static -> false
|
||||
MethodBridgeReceiver.Factory, MethodBridgeReceiver.Instance -> true
|
||||
}
|
||||
if (skipFirstKotlinParameter) {
|
||||
kotlinParameters.next()
|
||||
if (skipFirstKotlinParameter && allParameters.hasNext()) {
|
||||
allParameters.next()
|
||||
}
|
||||
|
||||
return this.valueParameters.map {
|
||||
when (it) {
|
||||
is MethodBridgeValueParameter.Mapped -> it to kotlinParameters.next()
|
||||
is MethodBridgeValueParameter.Mapped -> it to allParameters.next()
|
||||
is MethodBridgeValueParameter.SuspendCompletion,
|
||||
is MethodBridgeValueParameter.ErrorOutParameter,
|
||||
-> it to null
|
||||
}
|
||||
}.also { assert(!kotlinParameters.hasNext()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -329,7 +334,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"
|
||||
}
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithModality
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllMembers
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrProtocolName
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? {
|
||||
require(classKind == KtClassKind.OBJECT)
|
||||
if (!isVisibleInObjC()) return null
|
||||
|
||||
val superClass = getSuperClassSymbolNotAny()
|
||||
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
|
||||
val attributes = if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList()
|
||||
|
||||
val name = getObjCClassOrProtocolName()
|
||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||
val origin: ObjCExportStubOrigin = getObjCExportStubOrigin()
|
||||
val superProtocols: List<String> = superProtocols()
|
||||
val categoryName: String? = null
|
||||
val generics: List<ObjCGenericTypeDeclaration> = emptyList()
|
||||
val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList()
|
||||
val objectMembers = getDefaultMembers()
|
||||
|
||||
getAllMembers().flatMap { it.translateToObjCExportStubs() }.forEach {
|
||||
objectMembers.add(it)
|
||||
}
|
||||
|
||||
return ObjCInterfaceImpl(
|
||||
name.objCName,
|
||||
comment,
|
||||
origin,
|
||||
attributes,
|
||||
superProtocols,
|
||||
objectMembers,
|
||||
categoryName,
|
||||
generics,
|
||||
superName.objCName,
|
||||
superClassGenerics
|
||||
)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtClassOrObjectSymbol.getDefaultMembers(): MutableList<ObjCExportStub> {
|
||||
|
||||
val result = mutableListOf<ObjCExportStub>()
|
||||
val allocWithZoneParameter = ObjCParameter("zone", null, ObjCRawType("struct _NSZone *"), null)
|
||||
|
||||
result.add(
|
||||
ObjCMethod(null, null, false, ObjCInstanceType, listOf("alloc"), emptyList(), listOf("unavailable"))
|
||||
)
|
||||
|
||||
result.add(
|
||||
ObjCMethod(null, null, false, ObjCInstanceType, listOf("allocWithZone:"), listOf(allocWithZoneParameter), listOf("unavailable"))
|
||||
)
|
||||
|
||||
result.add(
|
||||
ObjCMethod(
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
ObjCInstanceType,
|
||||
listOf(getObjectInstanceSelector(this)),
|
||||
emptyList(),
|
||||
listOf(swiftNameAttribute("init()"))
|
||||
)
|
||||
)
|
||||
|
||||
result.add(
|
||||
ObjCProperty(
|
||||
name = ObjCPropertyNames.objectPropertyName,
|
||||
comment = null,
|
||||
type = toPropertyType(),
|
||||
propertyAttributes = listOf("class", "readonly"),
|
||||
getterName = getObjectPropertySelector(this),
|
||||
declarationAttributes = listOf(swiftNameAttribute(ObjCPropertyNames.objectPropertyName)),
|
||||
origin = null
|
||||
)
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Temp implementation
|
||||
* Use translateToObjCReferenceType() to make type
|
||||
* See also: [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapReferenceType]
|
||||
*/
|
||||
private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType(
|
||||
this.classIdIfNonLocal!!.shortClassName.asString(),
|
||||
emptyList()
|
||||
)
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getObjectInstanceSelector]
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun getObjectInstanceSelector(objectSymbol: KtClassOrObjectSymbol): String {
|
||||
return objectSymbol.getObjCClassOrProtocolName().objCName.lowercase()
|
||||
}
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getObjectPropertySelector]
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun getObjectPropertySelector(descriptor: KtClassOrObjectSymbol): String {
|
||||
val collides = ObjCPropertyNames.objectPropertyName == getObjectInstanceSelector(descriptor)
|
||||
return ObjCPropertyNames.objectPropertyName + (if (collides) "_" else "")
|
||||
}
|
||||
|
||||
+8
-6
@@ -4,6 +4,7 @@ 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.analysis.api.symbols.KtValueParameterSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.cKeywords
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
|
||||
@@ -23,13 +24,13 @@ internal fun KtFunctionLikeSymbol.translateToObjCParameters(baseMethodBridge: Me
|
||||
|
||||
val usedNames = mutableSetOf<String>()
|
||||
|
||||
valueParametersAssociated.forEach { (bridge: MethodBridgeValueParameter, parameter: KtTypeParameterSymbol?) ->
|
||||
valueParametersAssociated.forEach { (bridge: MethodBridgeValueParameter, parameter: KtValueParameterSymbol?) ->
|
||||
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)
|
||||
else -> parameter.name.toString()
|
||||
}
|
||||
}
|
||||
MethodBridgeValueParameter.ErrorOutParameter -> "error"
|
||||
@@ -40,7 +41,8 @@ internal fun KtFunctionLikeSymbol.translateToObjCParameters(baseMethodBridge: Me
|
||||
usedNames += uniqueName
|
||||
|
||||
val type = when (bridge) {
|
||||
is MethodBridgeValueParameter.Mapped -> TODO("Fetch KtType from KtTypeParameterSymbol: $parameter")
|
||||
is MethodBridgeValueParameter.Mapped ->
|
||||
parameter!!.returnType.translateToObjCReferenceType()
|
||||
MethodBridgeValueParameter.ErrorOutParameter ->
|
||||
ObjCPointerType(ObjCNullableReferenceType(ObjCClassType("NSError")), nullable = true)
|
||||
|
||||
@@ -48,9 +50,9 @@ internal fun KtFunctionLikeSymbol.translateToObjCParameters(baseMethodBridge: Me
|
||||
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)
|
||||
when (val type = this.returnType.translateToObjCReferenceType()) {
|
||||
is ObjCNonNullReferenceType -> ObjCNullableReferenceType(type, isNullableResult = false)
|
||||
is ObjCNullableReferenceType -> ObjCNullableReferenceType(type.nonNullType, isNullableResult = true)
|
||||
}
|
||||
}
|
||||
ObjCBlockPointerType(
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.backend.konan.objcexport.ObjCComment
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocolImpl
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.toNameAttributes
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDeclaredMembers
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.members
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol? {
|
||||
@@ -26,7 +26,7 @@ fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol? {
|
||||
// TODO: Check error type!
|
||||
val name = getObjCClassOrProtocolName()
|
||||
|
||||
val members = members().flatMap { it.translateToObjCExportStubs() }
|
||||
val members = getDeclaredMembers().flatMap { it.translateToObjCExportStubs() }
|
||||
|
||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||
|
||||
|
||||
+53
-21
@@ -5,6 +5,9 @@ import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isError
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.objCErrorType
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapReferenceType]
|
||||
@@ -20,30 +23,41 @@ internal fun KtType.translateToObjCReferenceType(): ObjCReferenceType {
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReferenceType {
|
||||
|
||||
/**
|
||||
* Simplified version of [org.jetbrains.kotlin.backend.konan.objcexport.CustomTypeMapper]
|
||||
*/
|
||||
val typesMap = mutableMapOf<ClassId, String>().apply {
|
||||
this[ClassId.topLevel(StandardNames.FqNames.list)] = "NSArray"
|
||||
this[ClassId.topLevel(StandardNames.FqNames.mutableList)] = "NSMutableArray"
|
||||
this[ClassId.topLevel(StandardNames.FqNames.set)] = "NSSet"
|
||||
this[ClassId.topLevel(StandardNames.FqNames.mutableSet)] = "MutableSet".getObjCKotlinStdlibClassOrProtocolName().objCName
|
||||
this[ClassId.topLevel(StandardNames.FqNames.map)] = "NSDictionary"
|
||||
this[ClassId.topLevel(StandardNames.FqNames.mutableMap)] = "MutableDictionary".getObjCKotlinStdlibClassOrProtocolName().objCName
|
||||
this[ClassId.topLevel(StandardNames.FqNames.string.toSafe())] = "NSString"
|
||||
}
|
||||
val classId = this.expandedClassSymbol?.classIdIfNonLocal
|
||||
val isInlined = false //TODO: replace when KT-65176 is implemented
|
||||
val isHidden = classId in hiddenTypes
|
||||
|
||||
NSNumberKind.entries.forEach {
|
||||
val classId = it.mappedKotlinClassId
|
||||
if (classId != null) {
|
||||
typesMap[classId] = classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName
|
||||
return if (isError) {
|
||||
objCErrorType
|
||||
} else if (isAny || isHidden || isInlined) {
|
||||
ObjCIdType
|
||||
} else {
|
||||
/**
|
||||
* Simplified version of [org.jetbrains.kotlin.backend.konan.objcexport.CustomTypeMapper]
|
||||
*/
|
||||
val typesMap = mutableMapOf<ClassId, String>().apply {
|
||||
this[ClassId.topLevel(StandardNames.FqNames.list)] = "NSArray"
|
||||
this[ClassId.topLevel(StandardNames.FqNames.mutableList)] = "NSMutableArray"
|
||||
this[ClassId.topLevel(StandardNames.FqNames.set)] = "NSSet"
|
||||
this[ClassId.topLevel(StandardNames.FqNames.mutableSet)] = "MutableSet".getObjCKotlinStdlibClassOrProtocolName().objCName
|
||||
this[ClassId.topLevel(StandardNames.FqNames.map)] = "NSDictionary"
|
||||
this[ClassId.topLevel(StandardNames.FqNames.mutableMap)] = "MutableDictionary".getObjCKotlinStdlibClassOrProtocolName().objCName
|
||||
this[ClassId.topLevel(StandardNames.FqNames.string.toSafe())] = "NSString"
|
||||
}
|
||||
|
||||
NSNumberKind.entries.forEach { number ->
|
||||
val numberClassId = number.mappedKotlinClassId
|
||||
if (numberClassId != null) {
|
||||
typesMap[numberClassId] = numberClassId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName
|
||||
}
|
||||
}
|
||||
|
||||
val typeName = typesMap[classId]
|
||||
?: classId!!.shortClassName.asString()
|
||||
.getObjCKotlinStdlibClassOrProtocolName().objCName //throw IllegalStateException("Unsupported mapping type for $this")
|
||||
|
||||
ObjCClassType(typeName)
|
||||
}
|
||||
|
||||
val typeName = typesMap[this.expandedClassSymbol?.classIdIfNonLocal]
|
||||
?: throw IllegalStateException("Unsupported mapping type for $this")
|
||||
|
||||
return ObjCClassType(typeName)
|
||||
}
|
||||
|
||||
private fun ObjCNonNullReferenceType.withNullabilityOf(kotlinType: KtType): ObjCReferenceType {
|
||||
@@ -53,3 +67,21 @@ private fun ObjCNonNullReferenceType.withNullabilityOf(kotlinType: KtType): ObjC
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Types to be "hidden" during mapping, i.e. represented as `id`.
|
||||
*
|
||||
* Currently contains super types of classes handled by custom type mappers.
|
||||
* Note: can be generated programmatically, but requires stdlib in this case.
|
||||
*/
|
||||
private val hiddenTypes: Set<ClassId> = listOf(
|
||||
"kotlin.Any",
|
||||
"kotlin.CharSequence",
|
||||
"kotlin.Comparable",
|
||||
"kotlin.Function",
|
||||
"kotlin.Number",
|
||||
"kotlin.collections.Collection",
|
||||
"kotlin.collections.Iterable",
|
||||
"kotlin.collections.MutableCollection",
|
||||
"kotlin.collections.MutableIterable"
|
||||
).map { ClassId.topLevel(FqName(it)) }.toSet()
|
||||
+5
-4
@@ -6,7 +6,8 @@
|
||||
package org.jetbrains.kotlin.objcexport.testUtils
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.backend.konan.tests.ObjCExportHeaderGeneratorTest.HeaderGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader
|
||||
import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportConfiguration
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportSession
|
||||
import org.jetbrains.kotlin.objcexport.translateToObjCHeader
|
||||
@@ -27,12 +28,12 @@ class AnalysisApiHeaderGeneratorExtension : ParameterResolver {
|
||||
}
|
||||
|
||||
object AnalysisApiHeaderGenerator : HeaderGenerator {
|
||||
override fun generateHeaders(root: File): String {
|
||||
override fun generateHeaders(root: File, configuration: HeaderGenerator.Configuration): ObjCHeader {
|
||||
val session = createStandaloneAnalysisApiSession(root.listFiles().orEmpty().filter { it.extension == "kt" })
|
||||
val (module, files) = session.modulesWithFiles.entries.single()
|
||||
return analyze(module) {
|
||||
KtObjCExportSession(KtObjCExportConfiguration()) {
|
||||
translateToObjCHeader(files.map { it as KtFile }).toString()
|
||||
KtObjCExportSession(KtObjCExportConfiguration(frameworkName = configuration.frameworkName)) {
|
||||
translateToObjCHeader(files.map { it as KtFile })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package org.jetbrains.kotlin.objcexport.tests
|
||||
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
||||
import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasErrorTypes
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.writeText
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class HasErrorTypesTest(
|
||||
private val headerGenerator: HeaderGenerator,
|
||||
) {
|
||||
|
||||
@TempDir
|
||||
private lateinit var tempDir: Path
|
||||
|
||||
@Test
|
||||
fun `test - no errors`() {
|
||||
val stubs = stubs(
|
||||
"""
|
||||
fun foo() = Unit
|
||||
class Foo {
|
||||
val myProperty: Int = 42
|
||||
fun myFunction(): Int = 42
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertFalse(stubs.hasErrorTypes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - property return type error`() {
|
||||
val stubs = stubs(
|
||||
"""
|
||||
val foo: Unresolved get() = error("stub")
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertTrue(stubs.hasErrorTypes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - function return type error`() {
|
||||
val stubs = stubs(
|
||||
"""
|
||||
fun foo(): Unresolved = error("stub")
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertTrue(stubs.hasErrorTypes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - nested member function return type error`() {
|
||||
val stubs = stubs(
|
||||
"""
|
||||
class Foo {
|
||||
fun foo(): Unresolved = error("stub")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertTrue(stubs.hasErrorTypes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - nested member property return type error`() {
|
||||
val stubs = stubs(
|
||||
"""
|
||||
class Foo {
|
||||
val foo: Unresolved get() = error("stub")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertTrue(stubs.hasErrorTypes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - nested error class property`() {
|
||||
val stubs = stubs(
|
||||
"""
|
||||
class A {
|
||||
class B {
|
||||
val e = error("error")
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertTrue(stubs.hasErrorTypes())
|
||||
}
|
||||
|
||||
private fun stubs(@Language("kotlin") sourceCode: String): List<ObjCExportStub> {
|
||||
val sourceFile = tempDir.resolve("sources.kt")
|
||||
sourceFile.writeText(sourceCode)
|
||||
return headerGenerator.generateHeaders(tempDir.toFile()).stubs
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -36,13 +36,15 @@ abstract class ObjCExportHeaderGenerator @InternalKotlinNativeApi constructor(
|
||||
|
||||
open val shouldExportKDoc = false
|
||||
|
||||
fun build(): List<String> = ObjCHeader(
|
||||
@InternalKotlinNativeApi
|
||||
fun buildHeader(): ObjCHeader = ObjCHeader(
|
||||
stubs = stubs,
|
||||
classForwardDeclarations = classForwardDeclarations,
|
||||
protocolForwardDeclarations = protocolForwardDeclarations,
|
||||
additionalImports = getAdditionalImports(),
|
||||
exportKDoc = shouldExportKDoc
|
||||
).lines
|
||||
)
|
||||
|
||||
fun build(): List<String> = buildHeader().render(shouldExportKDoc)
|
||||
|
||||
@InternalKotlinNativeApi
|
||||
fun buildInterface(): ObjCExportedInterface {
|
||||
|
||||
+9
-9
@@ -9,7 +9,6 @@ import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.backend.konan.tests.ObjCExportHeaderGeneratorTest
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
@@ -28,7 +27,7 @@ class Fe10HeaderGeneratorExtension : ParameterResolver, AfterEachCallback {
|
||||
}
|
||||
|
||||
override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Boolean {
|
||||
return parameterContext.parameter.type == ObjCExportHeaderGeneratorTest.HeaderGenerator::class.java
|
||||
return parameterContext.parameter.type == HeaderGenerator::class.java
|
||||
}
|
||||
|
||||
override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Any {
|
||||
@@ -43,15 +42,16 @@ class Fe10HeaderGeneratorExtension : ParameterResolver, AfterEachCallback {
|
||||
}
|
||||
}
|
||||
|
||||
private class Fe10HeaderGeneratorImpl(private val disposable: Disposable) :
|
||||
ObjCExportHeaderGeneratorTest.HeaderGenerator {
|
||||
override fun generateHeaders(root: File): String {
|
||||
val headerGenerator = createObjCExportHeaderGenerator(disposable, root)
|
||||
private class Fe10HeaderGeneratorImpl(private val disposable: Disposable) : HeaderGenerator {
|
||||
override fun generateHeaders(root: File, configuration: HeaderGenerator.Configuration): ObjCHeader {
|
||||
val headerGenerator = createObjCExportHeaderGenerator(disposable, root, configuration)
|
||||
headerGenerator.translateModuleDeclarations()
|
||||
return headerGenerator.build().joinToString(System.lineSeparator())
|
||||
return headerGenerator.buildHeader()
|
||||
}
|
||||
|
||||
private fun createObjCExportHeaderGenerator(disposable: Disposable, root: File): ObjCExportHeaderGenerator {
|
||||
private fun createObjCExportHeaderGenerator(
|
||||
disposable: Disposable, root: File, configuration: HeaderGenerator.Configuration,
|
||||
): ObjCExportHeaderGenerator {
|
||||
val mapper = ObjCExportMapper(
|
||||
unitSuspendFunctionExport = UnitSuspendFunctionObjCExport.DEFAULT
|
||||
)
|
||||
@@ -62,7 +62,7 @@ private class Fe10HeaderGeneratorImpl(private val disposable: Disposable) :
|
||||
local = false,
|
||||
problemCollector = ObjCExportProblemCollector.SILENT,
|
||||
configuration = object : ObjCExportNamer.Configuration {
|
||||
override val topLevelNamePrefix: String get() = ""
|
||||
override val topLevelNamePrefix: String get() = configuration.frameworkName
|
||||
override fun getAdditionalPrefix(module: ModuleDescriptor): String? = null
|
||||
override val objcGenerics: Boolean = true
|
||||
}
|
||||
|
||||
+75
-64
@@ -5,74 +5,85 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||
|
||||
data class ObjCHeader(val lines: List<String>) {
|
||||
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
||||
|
||||
data class ObjCHeader(
|
||||
@property:InternalKotlinNativeApi val stubs: List<ObjCExportStub>,
|
||||
@property:InternalKotlinNativeApi val classForwardDeclarations: Set<ObjCClassForwardDeclaration>,
|
||||
@property:InternalKotlinNativeApi val protocolForwardDeclarations: Set<String>,
|
||||
@property:InternalKotlinNativeApi val additionalImports: List<String>,
|
||||
) {
|
||||
|
||||
fun renderClassForwardDeclarations(): List<String> = buildList {
|
||||
if (classForwardDeclarations.isNotEmpty()) {
|
||||
add("@class ${
|
||||
classForwardDeclarations.joinToString {
|
||||
buildString {
|
||||
append(it.className)
|
||||
formatGenerics(this, it.typeDeclarations)
|
||||
}
|
||||
}
|
||||
};")
|
||||
add("")
|
||||
}
|
||||
}
|
||||
|
||||
fun renderProtocolForwardDeclarations(): List<String> = buildList {
|
||||
if (protocolForwardDeclarations.isNotEmpty()) {
|
||||
add("@protocol ${protocolForwardDeclarations.joinToString()};")
|
||||
add("")
|
||||
}
|
||||
}
|
||||
|
||||
fun render(exportKDoc: Boolean = true): List<String> {
|
||||
return buildList {
|
||||
addImports(foundationImports)
|
||||
addImports(additionalImports)
|
||||
add("")
|
||||
|
||||
addAll(renderClassForwardDeclarations())
|
||||
addAll(renderProtocolForwardDeclarations())
|
||||
|
||||
add("NS_ASSUME_NONNULL_BEGIN")
|
||||
add("#pragma clang diagnostic push")
|
||||
listOf(
|
||||
"-Wunknown-warning-option",
|
||||
|
||||
// Protocols don't have generics, classes do. So generated header may contain
|
||||
// overriding property with "incompatible" type, e.g. `Generic<T>`-typed property
|
||||
// overriding `Generic<id>`. Suppress these warnings:
|
||||
"-Wincompatible-property-type",
|
||||
|
||||
"-Wnullability"
|
||||
).forEach {
|
||||
add("#pragma clang diagnostic ignored \"$it\"")
|
||||
}
|
||||
add("")
|
||||
|
||||
// If _Nullable_result is not supported, then use _Nullable:
|
||||
add("#pragma push_macro(\"$objcNullableResultAttribute\")")
|
||||
add("#if !__has_feature(nullability_nullable_result)")
|
||||
add("#undef $objcNullableResultAttribute")
|
||||
add("#define $objcNullableResultAttribute $objcNullableAttribute")
|
||||
add("#endif")
|
||||
add("")
|
||||
|
||||
stubs.forEach {
|
||||
addAll(StubRenderer.render(it, exportKDoc))
|
||||
add("")
|
||||
}
|
||||
|
||||
add("#pragma pop_macro(\"$objcNullableResultAttribute\")")
|
||||
add("#pragma clang diagnostic pop")
|
||||
add("NS_ASSUME_NONNULL_END")
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return lines.joinToString(System.lineSeparator())
|
||||
return render().joinToString(System.lineSeparator())
|
||||
}
|
||||
}
|
||||
|
||||
fun ObjCHeader(
|
||||
stubs: List<ObjCExportStub>,
|
||||
classForwardDeclarations: Set<ObjCClassForwardDeclaration>,
|
||||
protocolForwardDeclarations: Set<String>,
|
||||
additionalImports: List<String> = emptyList(),
|
||||
exportKDoc: Boolean = false,
|
||||
): ObjCHeader = ObjCHeader(buildList {
|
||||
addImports(foundationImports)
|
||||
addImports(additionalImports)
|
||||
add("")
|
||||
|
||||
if (classForwardDeclarations.isNotEmpty()) {
|
||||
add("@class ${
|
||||
classForwardDeclarations.joinToString {
|
||||
buildString {
|
||||
append(it.className)
|
||||
formatGenerics(this, it.typeDeclarations)
|
||||
}
|
||||
}
|
||||
};")
|
||||
add("")
|
||||
}
|
||||
|
||||
if (protocolForwardDeclarations.isNotEmpty()) {
|
||||
add("@protocol ${protocolForwardDeclarations.joinToString()};")
|
||||
add("")
|
||||
}
|
||||
|
||||
add("NS_ASSUME_NONNULL_BEGIN")
|
||||
add("#pragma clang diagnostic push")
|
||||
listOf(
|
||||
"-Wunknown-warning-option",
|
||||
|
||||
// Protocols don't have generics, classes do. So generated header may contain
|
||||
// overriding property with "incompatible" type, e.g. `Generic<T>`-typed property
|
||||
// overriding `Generic<id>`. Suppress these warnings:
|
||||
"-Wincompatible-property-type",
|
||||
|
||||
"-Wnullability"
|
||||
).forEach {
|
||||
add("#pragma clang diagnostic ignored \"$it\"")
|
||||
}
|
||||
add("")
|
||||
|
||||
// If _Nullable_result is not supported, then use _Nullable:
|
||||
add("#pragma push_macro(\"$objcNullableResultAttribute\")")
|
||||
add("#if !__has_feature(nullability_nullable_result)")
|
||||
add("#undef $objcNullableResultAttribute")
|
||||
add("#define $objcNullableResultAttribute $objcNullableAttribute")
|
||||
add("#endif")
|
||||
add("")
|
||||
|
||||
stubs.forEach {
|
||||
addAll(StubRenderer.render(it, exportKDoc))
|
||||
add("")
|
||||
}
|
||||
|
||||
add("#pragma pop_macro(\"$objcNullableResultAttribute\")")
|
||||
add("#pragma clang diagnostic pop")
|
||||
add("NS_ASSUME_NONNULL_END")
|
||||
})
|
||||
|
||||
private fun MutableList<String>.addImports(imports: Iterable<String>) {
|
||||
imports.forEach {
|
||||
add("#import <$it>")
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.testUtils
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader
|
||||
import java.io.File
|
||||
|
||||
interface HeaderGenerator {
|
||||
|
||||
data class Configuration(
|
||||
val frameworkName: String = "",
|
||||
)
|
||||
|
||||
fun generateHeaders(root: File, configuration: Configuration = Configuration()): ObjCHeader
|
||||
}
|
||||
+2
-1
@@ -9,4 +9,5 @@ import java.io.File
|
||||
|
||||
val testDataDir = File("native/objcexport-header-generator/testData")
|
||||
val headersTestDataDir = testDataDir.resolve("headers")
|
||||
val baseDeclarationsDir = testDataDir.resolve("baseDeclarations")
|
||||
val baseDeclarationsDir = testDataDir.resolve("baseDeclarations")
|
||||
val forwardDeclarationsDir = testDataDir.resolve("forwardDeclarations")
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.backend.konan.tests
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.testUtils.forwardDeclarationsDir
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
import kotlin.test.fail
|
||||
|
||||
class ObjCExportForwardDeclarationsTest(
|
||||
private val generator: HeaderGenerator,
|
||||
) {
|
||||
|
||||
@Test
|
||||
fun `test - function returning interface`() {
|
||||
doTest(forwardDeclarationsDir.resolve("functionReturningInterface"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - function returning class`() {
|
||||
doTest(forwardDeclarationsDir.resolve("functionReturningClass"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - property returning interface`() {
|
||||
doTest(forwardDeclarationsDir.resolve("propertyReturningInterface"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - property returning class`() {
|
||||
doTest(forwardDeclarationsDir.resolve("propertyReturningClass"))
|
||||
}
|
||||
|
||||
private fun doTest(root: File) {
|
||||
if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory")
|
||||
val generatedHeaders = generator.generateHeaders(root)
|
||||
val renderedForwardDeclarations = buildString {
|
||||
generatedHeaders.renderClassForwardDeclarations().forEach(this::appendLine)
|
||||
generatedHeaders.renderProtocolForwardDeclarations().forEach(this::appendLine)
|
||||
}
|
||||
KotlinTestUtils.assertEqualsToFile(root.resolve("!${root.nameWithoutExtension}.h"), renderedForwardDeclarations)
|
||||
}
|
||||
}
|
||||
+17
-4
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.tests
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator.Configuration
|
||||
import org.jetbrains.kotlin.backend.konan.testUtils.headersTestDataDir
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -110,6 +112,11 @@ class ObjCExportHeaderGeneratorTest(val generator: HeaderGenerator) {
|
||||
doTest(headersTestDataDir.resolve("functionWithErrorType"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - functionWithErrorTypeAndFrameworkName`() {
|
||||
doTest(headersTestDataDir.resolve("functionWithErrorTypeAndFrameworkName"), Configuration(frameworkName = "shared"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - kdocWithBlockTags`() {
|
||||
doTest(headersTestDataDir.resolve("kdocWithBlockTags"))
|
||||
@@ -145,13 +152,19 @@ class ObjCExportHeaderGeneratorTest(val generator: HeaderGenerator) {
|
||||
doTest(headersTestDataDir.resolve("dispatchAndExtensionReceiverWithMustBeDocumentedAnnotation"))
|
||||
}
|
||||
|
||||
fun interface HeaderGenerator {
|
||||
fun generateHeaders(root: File): String
|
||||
@Test
|
||||
fun `test - classWithUnresolvedSuperType`() {
|
||||
doTest(headersTestDataDir.resolve("classWithUnresolvedSuperType"))
|
||||
}
|
||||
|
||||
private fun doTest(root: File) {
|
||||
@Test
|
||||
fun `test - classWithUnresolvedSuperTypeGenerics`() {
|
||||
doTest(headersTestDataDir.resolve("classWithUnresolvedSuperTypeGenerics"))
|
||||
}
|
||||
|
||||
private fun doTest(root: File, configuration: Configuration = Configuration()) {
|
||||
if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory")
|
||||
val generatedHeaders = generator.generateHeaders(root)
|
||||
val generatedHeaders = generator.generateHeaders(root, configuration).toString()
|
||||
KotlinTestUtils.assertEqualsToFile(root.resolve("!${root.nameWithoutExtension}.h"), generatedHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
@class Foo;
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(): Foo
|
||||
|
||||
class Foo
|
||||
+1
@@ -0,0 +1 @@
|
||||
@protocol Foo;
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(): Foo
|
||||
|
||||
interface Foo
|
||||
+1
@@ -0,0 +1 @@
|
||||
@class Foo;
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
val foo: Foo get() = TODO()
|
||||
|
||||
class Foo
|
||||
+1
@@ -0,0 +1 @@
|
||||
@protocol Foo;
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
val foo: Foo get() = TODO()
|
||||
|
||||
interface Foo
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSError.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
#pragma clang diagnostic ignored "-Wincompatible-property-type"
|
||||
#pragma clang diagnostic ignored "-Wnullability"
|
||||
|
||||
#pragma push_macro("_Nullable_result")
|
||||
#if !__has_feature(nullability_nullable_result)
|
||||
#undef _Nullable_result
|
||||
#define _Nullable_result _Nullable
|
||||
#endif
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
@interface Foo : Base
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
#pragma pop_macro("_Nullable_result")
|
||||
#pragma clang diagnostic pop
|
||||
NS_ASSUME_NONNULL_END
|
||||
+1
@@ -0,0 +1 @@
|
||||
class Foo : Unresolved()
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSError.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
@protocol Foo;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
#pragma clang diagnostic ignored "-Wincompatible-property-type"
|
||||
#pragma clang diagnostic ignored "-Wnullability"
|
||||
|
||||
#pragma push_macro("_Nullable_result")
|
||||
#if !__has_feature(nullability_nullable_result)
|
||||
#undef _Nullable_result
|
||||
#define _Nullable_result _Nullable
|
||||
#endif
|
||||
|
||||
@protocol Foo
|
||||
@required
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
@interface Bar : Base <Foo>
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
|
||||
@end
|
||||
|
||||
#pragma pop_macro("_Nullable_result")
|
||||
#pragma clang diagnostic pop
|
||||
NS_ASSUME_NONNULL_END
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
interface Foo<T>
|
||||
|
||||
class Bar : Foo<Unresolved>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSError.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSSet.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
@class ERROR;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
||||
#pragma clang diagnostic ignored "-Wincompatible-property-type"
|
||||
#pragma clang diagnostic ignored "-Wnullability"
|
||||
|
||||
#pragma push_macro("_Nullable_result")
|
||||
#if !__has_feature(nullability_nullable_result)
|
||||
#undef _Nullable_result
|
||||
#define _Nullable_result _Nullable
|
||||
#endif
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("FooKt")))
|
||||
@interface sharedFooKt : sharedBase
|
||||
+ (ERROR *)foo __attribute__((swift_name("foo()")));
|
||||
@end
|
||||
|
||||
@interface ERROR : sharedBase
|
||||
@end
|
||||
|
||||
#pragma pop_macro("_Nullable_result")
|
||||
#pragma clang diagnostic pop
|
||||
NS_ASSUME_NONNULL_END
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
fun foo() = Unresolved
|
||||
Reference in New Issue
Block a user