[ObjCExport] Implement rudimentary interface to ObjCProtocol translation
KT-64226
This commit is contained in:
committed by
Space Team
parent
aa3fa9e8c5
commit
9909940b24
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
data class KtObjCExportConfiguration(
|
||||
/**
|
||||
* Also used as top level prefix for declarations if present
|
||||
*/
|
||||
val frameworkName: String? = null,
|
||||
|
||||
/**
|
||||
* Should kdoc written by the user be available in the generated headers and stubs
|
||||
*/
|
||||
val exportKDoc: Boolean = true,
|
||||
)
|
||||
+16
-29
@@ -12,36 +12,23 @@ import org.jetbrains.kotlin.analysis.api.symbols.nameOrAnonymous
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportClassOrProtocolName
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportPropertyName
|
||||
|
||||
interface KtObjCExportNamer {
|
||||
context(KtAnalysisSession)
|
||||
fun getClassOrProtocolName(symbol: KtClassLikeSymbol): ObjCExportClassOrProtocolName
|
||||
|
||||
context(KtAnalysisSession)
|
||||
fun getPropertyName(symbol: KtPropertySymbol): ObjCExportPropertyName
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtClassLikeSymbol.getObjCClassOrProtocolName(): ObjCExportClassOrProtocolName {
|
||||
val resolvedObjCNameAnnotation = resolveObjCNameAnnotation()
|
||||
|
||||
return ObjCExportClassOrProtocolName(
|
||||
objCName = resolvedObjCNameAnnotation?.objCName ?: nameOrAnonymous.asString(),
|
||||
swiftName = resolvedObjCNameAnnotation?.swiftName ?: nameOrAnonymous.asString()
|
||||
)
|
||||
}
|
||||
|
||||
fun KtObjCExportNamer(): KtObjCExportNamer {
|
||||
return ObjCExportNamerImpl()
|
||||
}
|
||||
|
||||
private class ObjCExportNamerImpl : KtObjCExportNamer {
|
||||
context(KtAnalysisSession)
|
||||
override fun getClassOrProtocolName(symbol: KtClassLikeSymbol): ObjCExportClassOrProtocolName {
|
||||
val resolvedObjCNameAnnotation = symbol.resolveObjCNameAnnotation()
|
||||
|
||||
return ObjCExportClassOrProtocolName(
|
||||
objCName = resolvedObjCNameAnnotation?.objCName ?: symbol.nameOrAnonymous.asString(),
|
||||
swiftName = resolvedObjCNameAnnotation?.swiftName ?: symbol.nameOrAnonymous.asString()
|
||||
)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
override fun getPropertyName(symbol: KtPropertySymbol): ObjCExportPropertyName {
|
||||
val resolveObjCNameAnnotation = symbol.resolveObjCNameAnnotation()
|
||||
|
||||
return ObjCExportPropertyName(
|
||||
objCName = resolveObjCNameAnnotation?.objCName ?: symbol.name.asString(),
|
||||
swiftName = resolveObjCNameAnnotation?.swiftName ?: symbol.name.asString()
|
||||
)
|
||||
}
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtPropertySymbol.getObjCPropertyName(): ObjCExportPropertyName {
|
||||
val resolveObjCNameAnnotation = resolveObjCNameAnnotation()
|
||||
|
||||
return ObjCExportPropertyName(
|
||||
objCName = resolveObjCNameAnnotation?.objCName ?: name.asString(),
|
||||
swiftName = resolveObjCNameAnnotation?.swiftName ?: name.asString()
|
||||
)
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
interface KtObjCExportSession {
|
||||
val configuration: KtObjCExportConfiguration
|
||||
}
|
||||
|
||||
inline fun <T> KtObjCExportSession(
|
||||
configuration: KtObjCExportConfiguration,
|
||||
block: KtObjCExportSession.() -> T,
|
||||
): T {
|
||||
return object : KtObjCExportSession {
|
||||
override val configuration: KtObjCExportConfiguration = configuration
|
||||
}.block()
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.KtSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossiblyNamedSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStubOrigin
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.findKDocString
|
||||
|
||||
context(KtAnalysisSession)
|
||||
fun KtSymbol.objCStubOrigin(): ObjCExportStubOrigin {
|
||||
// TODO: Differentiate origins
|
||||
// TODO: Extract kdoc from deserialized symbols
|
||||
return ObjCExportStubOrigin.Source(
|
||||
name = let { it as? KtPossiblyNamedSymbol }?.name,
|
||||
psi = psi,
|
||||
kdoc = findKDocString()
|
||||
)
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.scopes.KtScope
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind.INTERFACE
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtScope.translateToObjCHeader(): ObjCHeader {
|
||||
val declarationsInScope = getAllSymbols()
|
||||
.mapNotNull { symbol -> symbol.translateToObjCExportStubOrNull() }
|
||||
.toList()
|
||||
|
||||
return ObjCHeader(
|
||||
stubs = declarationsInScope,
|
||||
classForwardDeclarations = emptySet(),
|
||||
protocolForwardDeclarations = declarationsInScope
|
||||
.filterIsInstance<ObjCProtocol>()
|
||||
.flatMap { it.superProtocols }
|
||||
.toSet(),
|
||||
additionalImports = emptyList(),
|
||||
exportKDoc = configuration.exportKDoc
|
||||
)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun KtSymbol.translateToObjCExportStubOrNull(): ObjCExportStub? {
|
||||
return when {
|
||||
this is KtClassOrObjectSymbol && classKind == INTERFACE -> translateToObjCProtocol()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.KtClassKind
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtClassType
|
||||
import org.jetbrains.kotlin.analysis.api.types.KtClassTypeQualifier
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCComment
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCProtocolImpl
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol {
|
||||
// TODO: check if this symbol shall be exposed in the first place
|
||||
require(classKind == KtClassKind.INTERFACE)
|
||||
|
||||
// TODO: Check error type!
|
||||
val name = getObjCClassOrProtocolName()
|
||||
|
||||
// TODO: Build members
|
||||
val members = emptyList<ObjCExportStub>()
|
||||
|
||||
val superProtocols = superTypes
|
||||
.asSequence()
|
||||
.filter { type -> !type.isAny }
|
||||
.mapNotNull { type -> type as? KtClassType }
|
||||
.flatMap { type -> type.qualifiers }
|
||||
.mapNotNull { qualifier -> qualifier as? KtClassTypeQualifier.KtResolvedClassTypeQualifier }
|
||||
.mapNotNull { it.symbol as? KtClassOrObjectSymbol }
|
||||
.filter { superInterface -> superInterface.classKind == KtClassKind.INTERFACE }
|
||||
.map { superInterface -> superInterface.getObjCClassOrProtocolName().objCName }
|
||||
.toList()
|
||||
|
||||
// TODO: Resolve comment
|
||||
val comment: ObjCComment? = null
|
||||
|
||||
return ObjCProtocolImpl(
|
||||
name = name.objCName,
|
||||
comment = comment,
|
||||
origin = this.objCStubOrigin(),
|
||||
attributes = emptyList(),
|
||||
superProtocols = superProtocols,
|
||||
members = members
|
||||
)
|
||||
}
|
||||
+14
-1
@@ -5,7 +5,12 @@
|
||||
|
||||
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.objcexport.KtObjCExportConfiguration
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportSession
|
||||
import org.jetbrains.kotlin.objcexport.translateToObjCHeader
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import org.junit.jupiter.api.extension.ParameterContext
|
||||
import org.junit.jupiter.api.extension.ParameterResolver
|
||||
@@ -23,6 +28,14 @@ class AnalysisApiHeaderGeneratorExtension : ParameterResolver {
|
||||
|
||||
object AnalysisApiHeaderGenerator : HeaderGenerator {
|
||||
override fun generateHeaders(root: File): String {
|
||||
TODO("Analysis Api based header generation in not yet implemented")
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.testUtils
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportConfiguration
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportSession
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
inline fun <T> analyzeWithObjCExport(
|
||||
useSiteKtElement: KtElement,
|
||||
configuration: KtObjCExportConfiguration = KtObjCExportConfiguration(),
|
||||
action: context(KtAnalysisSession, KtObjCExportSession) () -> T,
|
||||
): T = analyze(useSiteKtElement) {
|
||||
KtObjCExportSession(configuration) {
|
||||
action(this@analyze, this)
|
||||
}
|
||||
}
|
||||
+4
-6
@@ -5,12 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.objcexport.tests
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportClassOrProtocolName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportNamer
|
||||
import org.jetbrains.kotlin.objcexport.getObjCClassOrProtocolName
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.analyzeWithObjCExport
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@@ -18,19 +18,17 @@ class KtObjCExportNamerTest(
|
||||
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
|
||||
) {
|
||||
|
||||
private val namer = KtObjCExportNamer()
|
||||
|
||||
@Test
|
||||
fun `test - simple class`() {
|
||||
val foo = inlineSourceCodeAnalysis.createKtFile("class Foo")
|
||||
analyze(foo) {
|
||||
analyzeWithObjCExport(foo) {
|
||||
val fooSymbol = foo.getFileSymbol().getFileScope()
|
||||
.getClassifierSymbols(Name.identifier("Foo"))
|
||||
.single() as KtNamedClassOrObjectSymbol
|
||||
|
||||
assertEquals(
|
||||
ObjCExportClassOrProtocolName("Foo", "Foo"),
|
||||
namer.getClassOrProtocolName(fooSymbol)
|
||||
fooSymbol.getObjCClassOrProtocolName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user