[ObjCExport] Add companion object translation
KT-64953: Required part for enum translation
This commit is contained in:
committed by
Space Team
parent
acf2296590
commit
97f585dd25
+8
@@ -0,0 +1,8 @@
|
||||
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
|
||||
|
||||
internal val KtClassOrObjectSymbol.isCompanion: Boolean
|
||||
get() = classKind == KtClassKind.COMPANION_OBJECT
|
||||
+2
-2
@@ -173,6 +173,6 @@ private fun KtClassOrObjectSymbol.isInlined(): Boolean {
|
||||
}
|
||||
|
||||
private fun KtClassKind.isVisibleInObjC(): Boolean = when (this) {
|
||||
CLASS, ENUM_CLASS, INTERFACE, OBJECT -> true
|
||||
COMPANION_OBJECT, ANONYMOUS_OBJECT, ANNOTATION_CLASS -> false
|
||||
CLASS, ENUM_CLASS, INTERFACE, OBJECT, COMPANION_OBJECT -> true
|
||||
ANONYMOUS_OBJECT, ANNOTATION_CLASS -> false
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
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
|
||||
|
||||
/**
|
||||
* If object class has companion object it needs to have property which returns this companion.
|
||||
* To check whether class has companion object see [needsCompanionProperty]
|
||||
*/
|
||||
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"),
|
||||
getterName = propertyName,
|
||||
declarationAttributes = listOf(swiftNameAttribute(propertyName))
|
||||
)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isCompanion
|
||||
|
||||
/**
|
||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslator.needCompanionObjectProperty]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
internal val KtClassOrObjectSymbol.needsCompanionProperty: Boolean
|
||||
get() {
|
||||
return this.getStaticMemberScope().getClassifierSymbols()
|
||||
.any { (it as? KtClassOrObjectSymbol)?.isCompanion == true }
|
||||
}
|
||||
+4
-10
@@ -29,10 +29,12 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
val superProtocols: List<String> = superProtocols()
|
||||
val constructors = getMemberScope().getConstructors().filter { !it.hasExportForCompilerAnnotation }
|
||||
|
||||
val members: List<ObjCExportStub> = getMemberScope().getCallableSymbols().plus(constructors)
|
||||
val members = getMemberScope().getCallableSymbols().plus(constructors)
|
||||
.sortedWith(StableCallableOrder)
|
||||
.flatMap { it.translateToObjCExportStubs() }
|
||||
.toList()
|
||||
.toMutableList()
|
||||
|
||||
if (needsCompanionProperty) members.add(buildCompanionProperty())
|
||||
|
||||
val categoryName: String? = null
|
||||
|
||||
@@ -57,15 +59,7 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
||||
)
|
||||
}
|
||||
|
||||
private fun abbreviate(name: String): String {
|
||||
val normalizedName = name
|
||||
.replaceFirstChar(Char::uppercaseChar)
|
||||
.replace("-|\\.".toRegex(), "_")
|
||||
|
||||
val uppers = normalizedName.filterIndexed { index, character -> index == 0 || character.isUpperCase() }
|
||||
if (uppers.length >= 3) return uppers
|
||||
return normalizedName
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtNonErrorClassType.getSuperClassName(): ObjCExportClassOrProtocolName? {
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ fun translateToObjCHeader(files: List<KtFile>): ObjCHeader {
|
||||
KtClassKind.CLASS -> symbol.translateToObjCClass()
|
||||
KtClassKind.OBJECT -> symbol.translateToObjCObject()
|
||||
KtClassKind.ENUM_CLASS -> symbol.translateToObjCClass()
|
||||
KtClassKind.COMPANION_OBJECT -> symbol.translateToObjCObject()
|
||||
else -> return result
|
||||
} ?: return result
|
||||
|
||||
|
||||
+10
-9
@@ -6,24 +6,24 @@ 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.isCompanion
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? {
|
||||
require(classKind == KtClassKind.OBJECT)
|
||||
require(classKind == KtClassKind.OBJECT || classKind == KtClassKind.COMPANION_OBJECT)
|
||||
if (!isVisibleInObjC()) return null
|
||||
|
||||
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 attributes = (if (enumKind || final) listOf(OBJC_SUBCLASSING_RESTRICTED) else emptyList()) + name.toNameAttributes()
|
||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||
val origin: ObjCExportStubOrigin = getObjCExportStubOrigin()
|
||||
val superProtocols: List<String> = superProtocols()
|
||||
val categoryName: String? = null
|
||||
val generics: List<ObjCGenericTypeDeclaration> = emptyList()
|
||||
val objectMembers = getDefaultMembers()
|
||||
val objectMembers = getDefaultMembers().toMutableList()
|
||||
|
||||
val superClass = translateSuperClass()
|
||||
|
||||
@@ -47,7 +47,7 @@ fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? {
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtClassOrObjectSymbol.getDefaultMembers(): MutableList<ObjCExportStub> {
|
||||
private fun KtClassOrObjectSymbol.getDefaultMembers(): List<ObjCExportStub> {
|
||||
|
||||
val result = mutableListOf<ObjCExportStub>()
|
||||
val allocWithZoneParameter = ObjCParameter("zone", null, ObjCRawType("struct _NSZone *"), null)
|
||||
@@ -91,8 +91,9 @@ private fun KtClassOrObjectSymbol.getDefaultMembers(): MutableList<ObjCExportStu
|
||||
* Use translateToObjCReferenceType() to make type
|
||||
* See also: [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapReferenceType]
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType(
|
||||
this.classIdIfNonLocal!!.shortClassName.asString(),
|
||||
getObjCClassOrProtocolName().objCName,
|
||||
emptyList(),
|
||||
classIdIfNonLocal!!
|
||||
)
|
||||
@@ -102,7 +103,8 @@ private fun KtClassOrObjectSymbol.toPropertyType() = ObjCClassType(
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun getObjectInstanceSelector(objectSymbol: KtClassOrObjectSymbol): String {
|
||||
return objectSymbol.getObjCClassOrProtocolName().objCName.lowercase()
|
||||
return if (objectSymbol.isCompanion) ObjCPropertyNames.companionObjectPropertyName
|
||||
else objectSymbol.getObjCClassOrProtocolName().objCName.lowercase()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,5 +114,4 @@ context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun getObjectPropertySelector(descriptor: KtClassOrObjectSymbol): String {
|
||||
val collides = ObjCPropertyNames.objectPropertyName == getObjectInstanceSelector(descriptor)
|
||||
return ObjCPropertyNames.objectPropertyName + (if (collides) "_" else "")
|
||||
}
|
||||
|
||||
}
|
||||
+10
-6
@@ -104,15 +104,13 @@ internal fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReferenc
|
||||
}
|
||||
|
||||
if (fullyExpandedType is KtNonErrorClassType) {
|
||||
val typeName = fullyExpandedType.classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName
|
||||
val typeArguments = translateTypeArgumentsToObjC()
|
||||
|
||||
// TODO NOW: create type translation test
|
||||
if (classSymbol?.classKind == KtClassKind.INTERFACE) {
|
||||
return ObjCProtocolType(typeName, classId)
|
||||
return if (classSymbol?.classKind == KtClassKind.INTERFACE) {
|
||||
ObjCProtocolType(fullyExpandedType.objCTypeName, classId)
|
||||
} else {
|
||||
ObjCClassType(fullyExpandedType.objCTypeName, translateTypeArgumentsToObjC(), classId)
|
||||
}
|
||||
|
||||
return ObjCClassType(typeName, typeArguments, classId)
|
||||
}
|
||||
|
||||
if (fullyExpandedType is KtTypeParameterType) {
|
||||
@@ -135,6 +133,12 @@ internal fun KtType.mapToReferenceTypeIgnoringNullability(): ObjCNonNullReferenc
|
||||
return objCErrorType
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private val KtNonErrorClassType.objCTypeName: String
|
||||
get() {
|
||||
return getClassOrObjectSymbolByClassId(classId)?.getObjCClassOrProtocolName()?.objCName
|
||||
?: classId.shortClassName.asString().getObjCKotlinStdlibClassOrProtocolName().objCName
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtType.translateTypeArgumentsToObjC(): List<ObjCNonNullReferenceType> {
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package org.jetbrains.kotlin.objcexport.tests
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.objcexport.needsCompanionProperty
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NeedsCompanionPropertyTest(
|
||||
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
|
||||
) {
|
||||
|
||||
@Test
|
||||
fun `test - no companion`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class NoCompanion {
|
||||
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
analyze(file) {
|
||||
assertFalse(file.getClassOrFail("NoCompanion").needsCompanionProperty)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - empty companion`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class EmptyCompanion {
|
||||
companion object {}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
analyze(file) {
|
||||
assertTrue(file.getClassOrFail("EmptyCompanion").needsCompanionProperty)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - simple companion`() {
|
||||
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||
"""
|
||||
class SimpleCompanion {
|
||||
companion object {
|
||||
const val a = 42
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
analyze(file) {
|
||||
assertTrue(file.getClassOrFail("SimpleCompanion").needsCompanionProperty)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user