[ObjCExport] KtObjCExportHeaderGenerator: Separate 'requiresClassForwardDeclaration' from ObjCType.classId
There are two different objectives associated with this newly introduced extras: - originClassId: Used to track where a type came from; Will ensure that the translation queue will be populated to also translate potential dependency classes - requiresForwardDeclaration: Used to track if a certain type requires rendering as forward declaration KT-65891
This commit is contained in:
committed by
Space Team
parent
85854a6b8d
commit
a5baf42422
@@ -11,8 +11,9 @@ sourceSets {
|
||||
|
||||
dependencies {
|
||||
api(intellijCore())
|
||||
api(project(":native:base"))
|
||||
api(project(":core:compiler.common"))
|
||||
api(project(":kotlin-tooling-core"))
|
||||
api(project(":native:base"))
|
||||
|
||||
testApi(libs.junit.jupiter.api)
|
||||
testApi(libs.junit.jupiter.engine)
|
||||
|
||||
+2
-2
@@ -4,6 +4,7 @@ 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
|
||||
import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration
|
||||
|
||||
/**
|
||||
* Traverses stubs and returns true if [objCErrorType] is used as a return, parameter or property type
|
||||
@@ -44,5 +45,4 @@ internal val errorInterface
|
||||
superClassGenerics = emptyList()
|
||||
)
|
||||
|
||||
internal val objCErrorType = ObjCClassType(errorClassName, classId = null)
|
||||
internal val errorForwardClass = ObjCClassForwardDeclaration(errorClassName)
|
||||
internal val objCErrorType = ObjCClassType(errorClassName).withRequiresForwardDeclaration()
|
||||
|
||||
+10
-6
@@ -6,6 +6,8 @@ import org.jetbrains.kotlin.backend.konan.objcexport.ObjCClassType
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProperty
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.swiftNameAttribute
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCompanion
|
||||
import org.jetbrains.kotlin.objcexport.extras.withOriginClassId
|
||||
import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration
|
||||
|
||||
/**
|
||||
* If object class has companion object it needs to have property which returns this companion.
|
||||
@@ -13,18 +15,20 @@ import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCompanion
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtClassOrObjectSymbol.buildCompanionProperty(): ObjCProperty {
|
||||
|
||||
val companion = this.getStaticMemberScope().getClassifierSymbols().toList()
|
||||
.firstOrNull { (it as? KtClassOrObjectSymbol)?.isCompanion == true }
|
||||
|
||||
val typeName = (companion as KtClassOrObjectSymbol).getObjCClassOrProtocolName()
|
||||
val propertyName = ObjCPropertyNames.companionObjectPropertyName
|
||||
|
||||
return ObjCProperty(
|
||||
propertyName,
|
||||
null,
|
||||
null,
|
||||
ObjCClassType(typeName.objCName, classId = companion.classIdIfNonLocal),
|
||||
listOf("class", "readonly"),
|
||||
name = propertyName,
|
||||
comment = null,
|
||||
origin = null,
|
||||
type = ObjCClassType(typeName.objCName)
|
||||
.withRequiresForwardDeclaration()
|
||||
.withOriginClassId(companion.classIdIfNonLocal),
|
||||
propertyAttributes = listOf("class", "readonly"),
|
||||
getterName = propertyName,
|
||||
declarationAttributes = listOf(swiftNameAttribute(propertyName))
|
||||
)
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.extras
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCNullableReferenceType
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCReferenceType
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCType
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.tooling.core.extrasKeyOf
|
||||
|
||||
private val originClassIdKey = extrasKeyOf<ClassId>()
|
||||
|
||||
/**
|
||||
* Tracks the Kotlin origin associated with [this] [ObjCType].
|
||||
* Providing the [originClassId] can signal a header generation that the class associated with the [ClassId] should
|
||||
* also be translated for the header.
|
||||
*/
|
||||
internal val ObjCType.originClassId: ClassId?
|
||||
get() {
|
||||
extras[originClassIdKey]?.let { return it }
|
||||
|
||||
if (this is ObjCNullableReferenceType) {
|
||||
return this.nonNullType.extras[originClassIdKey]?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* See [originClassId]
|
||||
*/
|
||||
internal fun <T : ObjCReferenceType> T.withOriginClassId(classId: ClassId?): T = also { type ->
|
||||
if (classId != null) type.extras[originClassIdKey] = classId
|
||||
else type.extras.remove(originClassIdKey)
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.extras
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCReferenceType
|
||||
import org.jetbrains.kotlin.tooling.core.extrasKeyOf
|
||||
import org.jetbrains.kotlin.tooling.core.readWriteProperty
|
||||
|
||||
private val requiresForwardDeclarationKey = extrasKeyOf<Boolean>("isForwardDeclaration")
|
||||
|
||||
/**
|
||||
* Marks a type such that the generated header renders a forward declaration to this type when used.
|
||||
* - Default value: `false`.
|
||||
* - Example: All types used in function and method signature are expected to render forward declarations
|
||||
*/
|
||||
internal val ObjCReferenceType.requiresForwardDeclaration by requiresForwardDeclarationKey.readWriteProperty.notNull(false)
|
||||
|
||||
/**
|
||||
* ⚠️ Marks [this] [ObjCReferenceType] as 'requires forward declaration' and returns the same instance.
|
||||
* This method shall be used during the construction of a new type.
|
||||
* @see ObjCReferenceType.requiresForwardDeclaration
|
||||
*/
|
||||
internal fun <T : ObjCReferenceType> T.withRequiresForwardDeclaration(): T = also { type ->
|
||||
type.extras[requiresForwardDeclarationKey] = true
|
||||
}
|
||||
+72
-27
@@ -12,8 +12,9 @@ import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportHeaderGenerator.QueueElement
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.*
|
||||
import org.jetbrains.kotlin.objcexport.extras.originClassId
|
||||
import org.jetbrains.kotlin.objcexport.extras.requiresForwardDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
@@ -52,9 +53,19 @@ private class KtObjCExportHeaderGenerator {
|
||||
private val objCStubsByClassId = hashMapOf<ClassId, ObjCClass?>()
|
||||
|
||||
/**
|
||||
* The mutable aggregate of all entities that shall later be rendered as forward declarations
|
||||
* An index of already translated classes (by ObjC name)
|
||||
*/
|
||||
private val objCForwardDeclarations = mutableSetOf<ClassId>()
|
||||
private val objCStubsByClassName = hashMapOf<String, ObjCClass>()
|
||||
|
||||
/**
|
||||
* The mutable aggregate of all protocol names that shall later be rendered as forward declarations
|
||||
*/
|
||||
private val objCProtocolForwardDeclarations = mutableSetOf<String>()
|
||||
|
||||
/**
|
||||
* The mutable aggregate of all class names that shall later be rendered as forward declarations
|
||||
*/
|
||||
private val objCClassForwardDeclarations = mutableSetOf<String>()
|
||||
|
||||
/**
|
||||
* See [symbolDeque]:
|
||||
@@ -98,18 +109,25 @@ private class KtObjCExportHeaderGenerator {
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun translateFileSymbol(symbol: KtFileSymbol) {
|
||||
val objCFacades = listOfNotNull(symbol.getExtensionsFacade(), symbol.getTopLevelFacade()).ifEmpty { return }
|
||||
objCStubs += objCFacades
|
||||
objCFacades.forEach { enqueueDependencyClasses(it) }
|
||||
symbol.getExtensionsFacade()?.let { extensionFacade ->
|
||||
objCStubs += extensionFacade
|
||||
enqueueDependencyClasses(extensionFacade)
|
||||
objCClassForwardDeclarations += extensionFacade.name
|
||||
}
|
||||
|
||||
symbol.getTopLevelFacade()?.let { topLevelFacade ->
|
||||
objCStubs += topLevelFacade
|
||||
enqueueDependencyClasses(topLevelFacade)
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun translateClassOrObjectSymbol(symbol: KtClassOrObjectSymbol) {
|
||||
private fun translateClassOrObjectSymbol(symbol: KtClassOrObjectSymbol): ObjCClass? {
|
||||
/* No classId, no stubs ¯\_(ツ)_/¯ */
|
||||
val classId = symbol.classIdIfNonLocal ?: return
|
||||
val classId = symbol.classIdIfNonLocal ?: return null
|
||||
|
||||
/* Already processed this class, therefore nothing to do! */
|
||||
if (classId in objCStubsByClassId) return
|
||||
if (classId in objCStubsByClassId) return objCStubsByClassId[classId]
|
||||
|
||||
/**
|
||||
* Translate: Note: Even if the result was 'null', the classId will still be marked as 'handled' by adding it
|
||||
@@ -117,7 +135,7 @@ private class KtObjCExportHeaderGenerator {
|
||||
*/
|
||||
val objCClass = symbol.translateToObjCExportStub()
|
||||
objCStubsByClassId[classId] = objCClass
|
||||
objCClass ?: return
|
||||
objCClass ?: return null
|
||||
|
||||
/*
|
||||
To replicate the translation (and result stub order) of the K1 implementation:
|
||||
@@ -125,19 +143,24 @@ private class KtObjCExportHeaderGenerator {
|
||||
2) Super interface / superclass symbol export stubs (result of translation) have to be present in the stubs list before the
|
||||
original stub
|
||||
*/
|
||||
val superInterfaceOrClassSymbols = buildList {
|
||||
addAll(symbol.getDeclaredSuperInterfaceSymbols())
|
||||
addIfNotNull(symbol.getSuperClassSymbolNotAny())
|
||||
symbol.getDeclaredSuperInterfaceSymbols().forEach { superInterfaceSymbol ->
|
||||
translateClassOrObjectSymbol(superInterfaceSymbol)?.let {
|
||||
objCProtocolForwardDeclarations += it.name
|
||||
}
|
||||
}
|
||||
|
||||
superInterfaceOrClassSymbols.forEach { superInterfaceOrClassSymbol ->
|
||||
translateClassOrObjectSymbol(superInterfaceOrClassSymbol)
|
||||
symbol.getSuperClassSymbolNotAny()?.let { superClassSymbol ->
|
||||
translateClassOrObjectSymbol(superClassSymbol)?.let {
|
||||
objCClassForwardDeclarations += it.name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Note: It is important to add *this* stub to the result list only after translating/processing the superclass symbols */
|
||||
objCStubs += objCClass
|
||||
objCForwardDeclarations += superInterfaceOrClassSymbols.mapNotNull { it.classIdIfNonLocal }
|
||||
objCStubsByClassName[objCClass.name] = objCClass
|
||||
enqueueDependencyClasses(objCClass)
|
||||
return objCClass
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,32 +177,54 @@ private class KtObjCExportHeaderGenerator {
|
||||
* and `Array` has to be registered as forward declaration.
|
||||
*/
|
||||
private fun enqueueDependencyClasses(stub: ObjCExportStub) {
|
||||
symbolDeque += stub.closureSequence().mapNotNull { child ->
|
||||
symbolDeque += stub.closureSequence()
|
||||
.mapNotNull { child ->
|
||||
when (child) {
|
||||
is ObjCMethod -> child.returnType
|
||||
is ObjCParameter -> child.type
|
||||
is ObjCProperty -> child.type
|
||||
is ObjCTopLevel -> null
|
||||
}
|
||||
}.flatMap { type ->
|
||||
}
|
||||
.flatMap { type ->
|
||||
if (type is ObjCClassType) type.typeArguments + type
|
||||
else listOf(type)
|
||||
}.mapNotNull { if (it is ObjCReferenceType) it.classId else null }.onEach { objCForwardDeclarations += it }
|
||||
.map { QueueElement.Class(it) }.toList()
|
||||
}
|
||||
.filterIsInstance<ObjCReferenceType>()
|
||||
.onEach { type ->
|
||||
if (!type.requiresForwardDeclaration) return@onEach
|
||||
if (type is ObjCClassType) objCClassForwardDeclarations += type.className
|
||||
if (type is ObjCProtocolType) objCProtocolForwardDeclarations += type.protocolName
|
||||
}
|
||||
.mapNotNull { it.originClassId }
|
||||
.map(QueueElement::Class).toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* [objCClassForwardDeclarations] are recorded by their respective class name:
|
||||
* This method will resolve the objc interface that was translated, which is associated with the [className] and
|
||||
* build the respective [ObjCClassForwardDeclaration] from it.
|
||||
*
|
||||
* If no such class was explicitly translated a simple [ObjCClassForwardDeclaration] will be emitted that does not
|
||||
* carry any generics.
|
||||
*/
|
||||
private fun resolveObjCClassForwardDeclaration(className: String): ObjCClassForwardDeclaration {
|
||||
objCStubsByClassName[className]
|
||||
.let { it as? ObjCInterface }
|
||||
?.let { objCClass -> return ObjCClassForwardDeclaration(objCClass.name, objCClass.generics) }
|
||||
|
||||
return ObjCClassForwardDeclaration(className)
|
||||
}
|
||||
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun buildObjCHeader(): ObjCHeader {
|
||||
val hasErrorTypes = objCStubs.hasErrorTypes()
|
||||
|
||||
val resolvedObjCForwardDeclarations = objCForwardDeclarations.mapNotNull { classId -> objCStubsByClassId[classId] }.asSequence()
|
||||
val protocolForwardDeclarations = objCProtocolForwardDeclarations.toSet()
|
||||
|
||||
val protocolForwardDeclarations = resolvedObjCForwardDeclarations.filterIsInstance<ObjCProtocol>().map { it.name }.toSet()
|
||||
|
||||
val classForwardDeclarations = resolvedObjCForwardDeclarations.filterIsInstance<ObjCInterface>()
|
||||
.map { stub -> ObjCClassForwardDeclaration(stub.name, stub.generics) }
|
||||
.plus(listOfNotNull(errorForwardClass.takeIf { hasErrorTypes }))
|
||||
.plus(objCStubs.filterIsInstance<ObjCInterface>().filter { it.isExtensionsFacade }.map { ObjCClassForwardDeclaration(it.name) })
|
||||
val classForwardDeclarations = objCClassForwardDeclarations
|
||||
.map { className -> resolveObjCClassForwardDeclaration(className) }
|
||||
.toSet()
|
||||
|
||||
val stubs = (if (configuration.generateBaseDeclarationStubs) objCBaseDeclarations() else emptyList()).plus(objCStubs)
|
||||
|
||||
+4
-5
@@ -8,6 +8,8 @@ import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCompanion
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||
import org.jetbrains.kotlin.objcexport.extras.withOriginClassId
|
||||
import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? {
|
||||
@@ -83,11 +85,8 @@ private fun KtClassOrObjectSymbol.getDefaultMembers(): List<ObjCExportStub> {
|
||||
* See also: [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapReferenceType]
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType(
|
||||
getObjCClassOrProtocolName().objCName,
|
||||
emptyList(),
|
||||
classIdIfNonLocal!!
|
||||
)
|
||||
private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType(getObjCClassOrProtocolName().objCName, emptyList(),)
|
||||
.withRequiresForwardDeclaration().withOriginClassId(classIdIfNonLocal)
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getObjectInstanceSelector]
|
||||
|
||||
+5
-3
@@ -19,6 +19,8 @@ import org.jetbrains.kotlin.objcexport.analysisApiUtils.getInlineTargetTypeOrNul
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isError
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isObjCObjectType
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.objCErrorType
|
||||
import org.jetbrains.kotlin.objcexport.extras.withOriginClassId
|
||||
import org.jetbrains.kotlin.objcexport.extras.withRequiresForwardDeclaration
|
||||
|
||||
|
||||
/**
|
||||
@@ -107,10 +109,10 @@ internal fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReferenc
|
||||
|
||||
// TODO NOW: create type translation test
|
||||
return if (classSymbol?.classKind == KtClassKind.INTERFACE) {
|
||||
ObjCProtocolType(fullyExpandedType.objCTypeName, classId)
|
||||
ObjCProtocolType(fullyExpandedType.objCTypeName)
|
||||
} else {
|
||||
ObjCClassType(fullyExpandedType.objCTypeName, translateTypeArgumentsToObjC(), classId)
|
||||
}
|
||||
ObjCClassType(fullyExpandedType.objCTypeName, translateTypeArgumentsToObjC())
|
||||
}.withRequiresForwardDeclaration().withOriginClassId(classId)
|
||||
}
|
||||
|
||||
if (fullyExpandedType is KtTypeParameterType) {
|
||||
|
||||
+7
-12
@@ -6,10 +6,14 @@
|
||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.tooling.core.HasMutableExtras
|
||||
import org.jetbrains.kotlin.tooling.core.MutableExtras
|
||||
import org.jetbrains.kotlin.tooling.core.mutableExtrasOf
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
sealed class ObjCType {
|
||||
sealed class ObjCType : HasMutableExtras {
|
||||
override val extras: MutableExtras = mutableExtrasOf()
|
||||
|
||||
final override fun toString(): String = this.render()
|
||||
|
||||
abstract fun render(attrsAndName: String): String
|
||||
@@ -26,20 +30,13 @@ data class ObjCRawType(
|
||||
override fun render(attrsAndName: String): String = rawText.withAttrsAndName(attrsAndName)
|
||||
}
|
||||
|
||||
sealed class ObjCReferenceType : ObjCType() {
|
||||
@InternalKotlinNativeApi
|
||||
open val classId: ClassId? = null
|
||||
}
|
||||
|
||||
sealed class ObjCReferenceType : ObjCType()
|
||||
sealed class ObjCNonNullReferenceType : ObjCReferenceType()
|
||||
|
||||
data class ObjCNullableReferenceType(
|
||||
val nonNullType: ObjCNonNullReferenceType,
|
||||
val isNullableResult: Boolean = false,
|
||||
) : ObjCReferenceType() {
|
||||
|
||||
override val classId: ClassId? get() = nonNullType.classId
|
||||
|
||||
override fun render(attrsAndName: String): String {
|
||||
val attribute = if (isNullableResult) objcNullableResultAttribute else objcNullableAttribute
|
||||
return nonNullType.render(" $attribute".withAttrsAndName(attrsAndName))
|
||||
@@ -49,7 +46,6 @@ data class ObjCNullableReferenceType(
|
||||
data class ObjCClassType(
|
||||
val className: String,
|
||||
val typeArguments: List<ObjCNonNullReferenceType> = emptyList(),
|
||||
override val classId: ClassId? = null,
|
||||
) : ObjCNonNullReferenceType() {
|
||||
|
||||
|
||||
@@ -80,7 +76,6 @@ data class ObjCGenericTypeParameterUsage(
|
||||
|
||||
data class ObjCProtocolType(
|
||||
val protocolName: String,
|
||||
override val classId: ClassId? = null,
|
||||
) : ObjCNonNullReferenceType() {
|
||||
override fun render(attrsAndName: String) = "id<$protocolName>".withAttrsAndName(attrsAndName)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user