[ObjCExport] Support exported library modules
This new module gives the ability to resolve symbols from provided klib KtLibraryModules withing a Analysis Session. ^KT-65327 Fixed
This commit is contained in:
committed by
Space Team
parent
b5b7e5f262
commit
10cf97f05b
@@ -14,8 +14,10 @@ dependencies {
|
||||
api(project(":analysis:analysis-api"))
|
||||
api(project(":compiler:psi"))
|
||||
api(project(":native:objcexport-header-generator"))
|
||||
|
||||
implementation(project(":core:compiler.common.native"))
|
||||
implementation(project(":kotlin-util-klib"))
|
||||
implementation(project(":native:analysis-api-klib-reader"))
|
||||
|
||||
testImplementation(projectTests(":native:objcexport-header-generator"))
|
||||
testApi(project(":analysis:analysis-api-standalone"))
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.klib.reader.*
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getAllClassOrObjectSymbols
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.tooling.core.withClosure
|
||||
|
||||
internal interface KtObjCExportFile {
|
||||
val fileName: String
|
||||
val packageFqName: FqName
|
||||
|
||||
context(KtAnalysisSession)
|
||||
fun resolve(): KtResolvedObjCExportFile
|
||||
}
|
||||
|
||||
/**
|
||||
* The 'resolved' version of the [KtObjCExportFile].
|
||||
* 'Resolved' means that all symbols associated with the file are loaded and available
|
||||
* under [classifierSymbols] and [callableSymbols].
|
||||
*/
|
||||
data class KtResolvedObjCExportFile(
|
||||
val fileName: String,
|
||||
val packageFqName: FqName,
|
||||
val classifierSymbols: List<KtClassOrObjectSymbol>,
|
||||
val callableSymbols: List<KtCallableSymbol>,
|
||||
)
|
||||
|
||||
/* Factory functions */
|
||||
|
||||
internal fun KtObjCExportFile(file: KtFile): KtObjCExportFile {
|
||||
return KtPsiObjCExportFile(file)
|
||||
}
|
||||
|
||||
internal fun createKtObjCExportFiles(addresses: Iterable<KlibDeclarationAddress>): List<KtObjCExportFile> {
|
||||
val addressesByPackageName = addresses.groupBy { it.packageFqName }
|
||||
return addressesByPackageName.flatMap { (packageName, addresses) ->
|
||||
val addressesByFile = addresses.groupBy { it.sourceFileName }
|
||||
addressesByFile.mapNotNull { (fileName, addresses) ->
|
||||
fileName ?: return@mapNotNull null
|
||||
KtKlibObjCExportFile(FileUtil.getNameWithoutExtension(fileName), packageName, addresses.toSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Private Implementations */
|
||||
|
||||
private class KtPsiObjCExportFile(
|
||||
private val file: KtFile,
|
||||
) : KtObjCExportFile {
|
||||
override val fileName: String
|
||||
get() = FileUtil.getNameWithoutExtension(file.name)
|
||||
|
||||
override val packageFqName: FqName
|
||||
get() = file.packageFqName
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other === this) return true
|
||||
if (other !is KtPsiObjCExportFile) return false
|
||||
if (other.file != this.file) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return file.hashCode()
|
||||
}
|
||||
|
||||
/**
|
||||
* See [KtResolvedObjCExportFile]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
override fun resolve(): KtResolvedObjCExportFile {
|
||||
val symbol = file.getFileSymbol()
|
||||
return KtResolvedObjCExportFile(
|
||||
fileName = fileName,
|
||||
packageFqName = packageFqName,
|
||||
classifierSymbols = symbol.getAllClassOrObjectSymbols(),
|
||||
callableSymbols = symbol.getFileScope().getCallableSymbols().toList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class KtKlibObjCExportFile(
|
||||
override val fileName: String,
|
||||
override val packageFqName: FqName,
|
||||
private val addresses: Set<KlibDeclarationAddress>,
|
||||
) : KtObjCExportFile {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other === this) return true
|
||||
if (other !is KtKlibObjCExportFile) return false
|
||||
if (other.fileName != this.fileName) return false
|
||||
if (other.packageFqName != this.packageFqName) return false
|
||||
if (other.addresses != this.addresses) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = fileName.hashCode()
|
||||
result = 31 * result + packageFqName.hashCode()
|
||||
result = 31 * result + addresses.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
override fun resolve(): KtResolvedObjCExportFile {
|
||||
val classifierAddresses = addresses.filterIsInstance<KlibClassAddress>()
|
||||
val callableAddresses = addresses.filterIsInstance<KlibCallableAddress>()
|
||||
|
||||
return KtResolvedObjCExportFile(
|
||||
fileName = fileName,
|
||||
packageFqName = packageFqName,
|
||||
classifierSymbols = classifierAddresses
|
||||
.mapNotNull { classAddress -> classAddress.getClassOrObjectSymbol() }
|
||||
.withClosure<KtClassOrObjectSymbol> { symbol ->
|
||||
symbol.getMemberScope().getClassifierSymbols().filterIsInstance<KtClassOrObjectSymbol>().asIterable()
|
||||
}.toList(),
|
||||
callableSymbols = callableAddresses.flatMap { address ->
|
||||
address.getCallableSymbols()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -1,13 +1,10 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportFileName
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.toIdentifier
|
||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getFileName
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
internal fun KtFileSymbol.getObjCFileClassOrProtocolName(): ObjCExportFileName? {
|
||||
val fileName = getFileName() ?: return null
|
||||
internal fun KtResolvedObjCExportFile.getObjCFileClassOrProtocolName(): ObjCExportFileName {
|
||||
return (fileName + "Kt").toIdentifier().getObjCFileName()
|
||||
}
|
||||
+6
-12
@@ -1,7 +1,6 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInterface
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInterfaceImpl
|
||||
|
||||
@@ -45,27 +44,22 @@ internal val ObjCInterface.isExtensionsFacade: Boolean
|
||||
*
|
||||
* Where `Foo` would be the "top level interface file extensions facade" returned by this function.
|
||||
*
|
||||
* See related [getTopLevelFacade]
|
||||
* See related [translateToObjCTopLevelFacade]
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFileSymbol.getExtensionFacades(): List<ObjCInterface> {
|
||||
|
||||
val extensions = getFileScope()
|
||||
.getCallableSymbols().filter { it.isExtension }
|
||||
.toList()
|
||||
fun KtResolvedObjCExportFile.translateToObjCExtensionFacades(): List<ObjCInterface> {
|
||||
val extensions = callableSymbols
|
||||
.filter { it.isExtension }
|
||||
.sortedWith(StableCallableOrder)
|
||||
.ifEmpty { return emptyList() }
|
||||
.groupBy {
|
||||
val classSymbol = it.receiverParameter?.type?.expandedClassSymbol
|
||||
classSymbol?.getObjCClassOrProtocolName()?.objCName
|
||||
}
|
||||
.mapNotNull { (key, value) ->
|
||||
if (key == null) return@mapNotNull null else key to value
|
||||
}
|
||||
|
||||
return extensions.map { (objCName, extensionSymbols) ->
|
||||
return extensions.mapNotNull { (objCName, extensionSymbols) ->
|
||||
ObjCInterfaceImpl(
|
||||
name = objCName,
|
||||
name = objCName ?: return@mapNotNull null,
|
||||
comment = null,
|
||||
origin = null,
|
||||
attributes = emptyList(),
|
||||
+59
-55
@@ -6,22 +6,33 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.klib.reader.readKlibDeclarationAddresses
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.allDirectDependencies
|
||||
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.objcexport.extras.throwsAnnotationClassIds
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.tooling.core.closure
|
||||
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun translateToObjCHeader(files: List<KtFile>): ObjCHeader {
|
||||
val generator = KtObjCExportHeaderGenerator()
|
||||
generator.translateAll(files.sortedWith(StableFileOrder).map { QueueElement.File(it) })
|
||||
|
||||
val klibDeclarationAddresses = useSiteModule.closure { module -> module.allDirectDependencies().asIterable() }
|
||||
.filterIsInstance<KtLibraryModule>()
|
||||
.filter { it.getObjCKotlinModuleName() in configuration.exportedModuleNames }
|
||||
.flatMap { it.readKlibDeclarationAddresses().orEmpty() }
|
||||
|
||||
val unresolvedFiles = files.map { ktFile -> KtObjCExportFile(ktFile) } +
|
||||
createKtObjCExportFiles(klibDeclarationAddresses)
|
||||
|
||||
generator.translateAll(unresolvedFiles.sortedWith(StableFileOrder))
|
||||
return generator.buildObjCHeader()
|
||||
}
|
||||
|
||||
@@ -32,16 +43,15 @@ fun translateToObjCHeader(files: List<KtFile>): ObjCHeader {
|
||||
*
|
||||
* can be made.
|
||||
*
|
||||
* Functions inside this class will have side effects such as mutating the [symbolDeque] or adding results to the [objCStubs]
|
||||
* Functions inside this class will have side effects such as mutating the [classDeque] or adding results to the [objCStubs]
|
||||
*/
|
||||
private class KtObjCExportHeaderGenerator {
|
||||
/**
|
||||
* Represents all elements that still have to be processed and translated.
|
||||
* So far this only includes references to top level entities (such as files or classes):
|
||||
* Note: Top level functions and properties will be translated as part of the file.
|
||||
* See [translateToObjCTopLevelInterfaceFileFacades]
|
||||
* Represents a queue containing pointers to all classes that are 'to be translated later'.
|
||||
* This happens, e.g., when a class is referenced inside a callable signature. Such 'dependency types' are to be
|
||||
* translated
|
||||
*/
|
||||
private val symbolDeque = ArrayDeque<QueueElement>()
|
||||
private val classDeque = ArrayDeque<ClassId>()
|
||||
|
||||
/**
|
||||
* The mutable aggregate of the already translated elements
|
||||
@@ -68,57 +78,57 @@ private class KtObjCExportHeaderGenerator {
|
||||
*/
|
||||
private val objCClassForwardDeclarations = mutableSetOf<String>()
|
||||
|
||||
/**
|
||||
* See [symbolDeque]:
|
||||
* All top level 'to do' elements will be represented as [QueueElement] and later handled by the [translateAll] function.
|
||||
*/
|
||||
sealed class QueueElement {
|
||||
class File(val psi: KtFile) : QueueElement()
|
||||
class Class(val classId: ClassId) : QueueElement()
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun translateAll(symbolProviders: List<QueueElement>) {
|
||||
symbolDeque.addAll(symbolProviders)
|
||||
fun translateAll(files: List<KtObjCExportFile>) {
|
||||
/**
|
||||
* Step 1: Translate classifiers (class, interface, object, ...)
|
||||
*/
|
||||
files.forEach { file ->
|
||||
translateFileClassifiers(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 2: Translate file facades (see [translateToTopLevelFileFacade], [translateToExtensionFacade])
|
||||
* This step has to be done after all classifiers were translated to match the translation order of K1
|
||||
*/
|
||||
files.forEach { file ->
|
||||
translateFileFacades(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 3: Translate dependency classes referenced by Step 1 and Step 2
|
||||
* Note: Transitive dependencies will still add to this queue and will be processed until we're finished
|
||||
*/
|
||||
while (true) {
|
||||
val symbolProvider = symbolDeque.removeFirstOrNull() ?: break
|
||||
translateElement(symbolProvider)
|
||||
translateClass(classDeque.removeFirstOrNull() ?: break)
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun translateElement(element: QueueElement) = when (element) {
|
||||
is QueueElement.Class -> translateClassElement(element)
|
||||
is QueueElement.File -> translateFileElement(element)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun translateClassElement(element: QueueElement.Class) {
|
||||
val classOrObjectSymbol = getClassOrObjectSymbolByClassId(element.classId) ?: return
|
||||
private fun translateClass(classId: ClassId) {
|
||||
val classOrObjectSymbol = getClassOrObjectSymbolByClassId(classId) ?: return
|
||||
translateClassOrObjectSymbol(classOrObjectSymbol)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun translateFileElement(element: QueueElement.File) {
|
||||
val fileSymbol = element.psi.getFileSymbol()
|
||||
fileSymbol.getAllClassOrObjectSymbols().sortedWith(StableClassifierOrder).forEach { classOrObjectSymbol ->
|
||||
private fun translateFileClassifiers(file: KtObjCExportFile) {
|
||||
val resolvedFile = file.resolve()
|
||||
resolvedFile.classifierSymbols.sortedWith(StableClassifierOrder).forEach { classOrObjectSymbol ->
|
||||
translateClassOrObjectSymbol(classOrObjectSymbol)
|
||||
}
|
||||
translateFileSymbol(fileSymbol)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
private fun translateFileSymbol(symbol: KtFileSymbol) {
|
||||
symbol.getExtensionFacades().let { extensionFacades ->
|
||||
extensionFacades.forEach { facade ->
|
||||
objCStubs += facade
|
||||
enqueueDependencyClasses(facade)
|
||||
objCClassForwardDeclarations += facade.name
|
||||
}
|
||||
private fun translateFileFacades(file: KtObjCExportFile) {
|
||||
val resolvedFile = file.resolve()
|
||||
|
||||
resolvedFile.translateToObjCExtensionFacades().forEach { facade ->
|
||||
objCStubs += facade
|
||||
enqueueDependencyClasses(facade)
|
||||
objCClassForwardDeclarations += facade.name
|
||||
}
|
||||
|
||||
symbol.getTopLevelFacade()?.let { topLevelFacade ->
|
||||
resolvedFile.translateToObjCTopLevelFacade()?.let { topLevelFacade ->
|
||||
objCStubs += topLevelFacade
|
||||
enqueueDependencyClasses(topLevelFacade)
|
||||
}
|
||||
@@ -146,15 +156,13 @@ 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
|
||||
*/
|
||||
symbol.getDeclaredSuperInterfaceSymbols()
|
||||
.filter { it.isVisibleInObjC() }
|
||||
.forEach { superInterfaceSymbol ->
|
||||
translateClassOrObjectSymbol(superInterfaceSymbol)?.let {
|
||||
objCProtocolForwardDeclarations += it.name
|
||||
}
|
||||
symbol.getDeclaredSuperInterfaceSymbols().filter { it.isVisibleInObjC() }.forEach { superInterfaceSymbol ->
|
||||
translateClassOrObjectSymbol(superInterfaceSymbol)?.let {
|
||||
objCProtocolForwardDeclarations += it.name
|
||||
}
|
||||
}
|
||||
|
||||
symbol.getSuperClassSymbolNotAny()?.let { superClassSymbol ->
|
||||
symbol.getSuperClassSymbolNotAny()?.takeIf { it.isVisibleInObjC() }?.let { superClassSymbol ->
|
||||
translateClassOrObjectSymbol(superClassSymbol)?.let {
|
||||
objCClassForwardDeclarations += it.name
|
||||
}
|
||||
@@ -182,12 +190,10 @@ private class KtObjCExportHeaderGenerator {
|
||||
* and `Array` has to be registered as forward declaration.
|
||||
*/
|
||||
private fun enqueueDependencyClasses(stub: ObjCExportStub) {
|
||||
|
||||
symbolDeque += stub.closureSequence()
|
||||
classDeque += stub.closureSequence()
|
||||
.flatMap { child -> child.throwsAnnotationClassIds.orEmpty() }
|
||||
.map { QueueElement.Class(it) }
|
||||
|
||||
symbolDeque += stub.closureSequence()
|
||||
classDeque += stub.closureSequence()
|
||||
.mapNotNull { child ->
|
||||
when (child) {
|
||||
is ObjCMethod -> child.returnType
|
||||
@@ -208,7 +214,6 @@ private class KtObjCExportHeaderGenerator {
|
||||
if (nonNullType is ObjCProtocolType) objCProtocolForwardDeclarations += nonNullType.protocolName
|
||||
}
|
||||
.mapNotNull { it.originClassId }
|
||||
.map(QueueElement::Class).toList()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,7 +232,6 @@ private class KtObjCExportHeaderGenerator {
|
||||
return ObjCClassForwardDeclaration(className)
|
||||
}
|
||||
|
||||
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun buildObjCHeader(): ObjCHeader {
|
||||
val hasErrorTypes = objCStubs.hasErrorTypes()
|
||||
|
||||
+3
-5
@@ -6,7 +6,6 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInterface
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInterfaceImpl
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.toNameAttributes
|
||||
@@ -43,18 +42,17 @@ import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrPr
|
||||
*
|
||||
* Where `FooKt` would be the "top level interface file facade" returned by this function.
|
||||
*
|
||||
* See related [getExtensionFacades]
|
||||
* See related [translateToObjCExtensionFacades]
|
||||
*/
|
||||
context(KtAnalysisSession, KtObjCExportSession)
|
||||
fun KtFileSymbol.getTopLevelFacade(): ObjCInterface? {
|
||||
val extensions = getFileScope().getCallableSymbols()
|
||||
fun KtResolvedObjCExportFile.translateToObjCTopLevelFacade(): ObjCInterface? {
|
||||
val extensions = callableSymbols
|
||||
.filter { !it.isExtension }
|
||||
.toList()
|
||||
.sortedWith(StableCallableOrder)
|
||||
.ifEmpty { return null }
|
||||
|
||||
val fileName = getObjCFileClassOrProtocolName()
|
||||
?: throw IllegalStateException("File '$this' cannot be translated without file name")
|
||||
|
||||
return ObjCInterfaceImpl(
|
||||
name = fileName.objCName,
|
||||
+3
-15
@@ -6,11 +6,10 @@
|
||||
package org.jetbrains.kotlin.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
internal val StableFileOrder: Comparator<KtFile>
|
||||
get() = compareBy<KtFile> { file -> file.packageFqName.asString() }
|
||||
.thenComparing { file -> file.name }
|
||||
internal val StableFileOrder: Comparator<KtObjCExportFile>
|
||||
get() = compareBy<KtObjCExportFile> { file -> file.packageFqName.asString() }
|
||||
.thenComparing { file -> file.fileName }
|
||||
|
||||
internal val StablePropertyOrder: Comparator<KtPropertySymbol> = compareBy { it.name }
|
||||
|
||||
@@ -55,17 +54,6 @@ internal val StableCallableOrder: Comparator<KtCallableSymbol> = compareBy<KtCal
|
||||
.thenComparing(StablePropertyOrder)
|
||||
.thenComparing(StableFunctionOrder)
|
||||
|
||||
internal val StableSymbolOrder: Comparator<KtSymbol> = compareBy<KtSymbol> { symbol ->
|
||||
when (symbol) {
|
||||
is KtFileSymbol -> 0
|
||||
is KtClassifierSymbol -> 1
|
||||
is KtCallableSymbol -> 2
|
||||
else -> Int.MAX_VALUE
|
||||
}
|
||||
}
|
||||
.thenComparing(StableClassifierOrder)
|
||||
.thenComparing(StableCallableOrder)
|
||||
|
||||
private inline fun <T, reified R> Comparator<T>.thenComparing(comparator: Comparator<R>): Comparator<T> where R : T {
|
||||
return thenComparing { a, b ->
|
||||
if (a is R && b is R) comparator.compare(a, b) else 0
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ object AnalysisApiHeaderGenerator : HeaderGenerator {
|
||||
KtObjCExportConfiguration(
|
||||
frameworkName = configuration.frameworkName,
|
||||
generateBaseDeclarationStubs = configuration.generateBaseDeclarationStubs,
|
||||
exportedModuleNames = setOf(defaultKotlinSourceModuleName)
|
||||
exportedModuleNames = setOf(defaultKotlinSourceModuleName) + configuration.exportedDependencyModuleNames
|
||||
)
|
||||
) {
|
||||
translateToObjCHeader(files.map { it as KtFile })
|
||||
|
||||
+5
-4
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.objcexport.tests
|
||||
|
||||
import org.jetbrains.kotlin.objcexport.KtObjCExportFile
|
||||
import org.jetbrains.kotlin.objcexport.StableFileOrder
|
||||
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -23,8 +24,8 @@ class TranslationOrderTest(
|
||||
sourceFile("C.kt", "package com.c")
|
||||
}
|
||||
|
||||
val unsorted = listOf(files.getValue("C.kt"), files.getValue("B.kt"), files.getValue("A.kt"))
|
||||
val sorted = listOf(files.getValue("A.kt"), files.getValue("B.kt"), files.getValue("C.kt"))
|
||||
val unsorted = listOf(files.getValue("C.kt"), files.getValue("B.kt"), files.getValue("A.kt")).map(::KtObjCExportFile)
|
||||
val sorted = listOf(files.getValue("A.kt"), files.getValue("B.kt"), files.getValue("C.kt")).map(::KtObjCExportFile)
|
||||
|
||||
assertNotEquals(sorted, unsorted)
|
||||
assertEquals(sorted, unsorted.sortedWith(StableFileOrder))
|
||||
@@ -38,8 +39,8 @@ class TranslationOrderTest(
|
||||
sourceFile("B2.kt", "package com.b")
|
||||
}
|
||||
|
||||
val unsorted = listOf(files.getValue("B2.kt"), files.getValue("A.kt"), files.getValue("B1.kt"))
|
||||
val sorted = listOf(files.getValue("A.kt"), files.getValue("B1.kt"), files.getValue("B2.kt"))
|
||||
val unsorted = listOf(files.getValue("B2.kt"), files.getValue("A.kt"), files.getValue("B1.kt")).map(::KtObjCExportFile)
|
||||
val sorted = listOf(files.getValue("A.kt"), files.getValue("B1.kt"), files.getValue("B2.kt")).map(::KtObjCExportFile)
|
||||
|
||||
assertNotEquals(sorted, unsorted)
|
||||
assertEquals(sorted, unsorted.sortedWith(StableFileOrder))
|
||||
|
||||
+3
-2
@@ -69,14 +69,15 @@ class ObjCExportDependenciesHeaderGeneratorTest(
|
||||
* Requires being able to use AA to iterate over symbols to 'export' the dependency
|
||||
*/
|
||||
@Test
|
||||
@TodoAnalysisApi
|
||||
fun `test - exportedAndNotExportedDependency`() {
|
||||
doTest(
|
||||
dependenciesDir.resolve("exportedAndNotExportedDependency"), configuration = HeaderGenerator.Configuration(
|
||||
frameworkName = "MyApp",
|
||||
generateBaseDeclarationStubs = true,
|
||||
dependencies = listOf(testLibraryAKlibFile, testLibraryBKlibFile),
|
||||
exportedDependencyModuleNames = setOf("org.jetbrains.kotlin:testLibraryA")
|
||||
exportedDependencyModuleNames = setOf(
|
||||
"org.jetbrains.kotlin:testLibraryA", "testLibraryA"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+7
@@ -157,6 +157,13 @@ __attribute__((swift_name("FooKt")))
|
||||
+ (MyAppTLBMyLibraryB *)foo __attribute__((swift_name("foo()")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("TopLevelCallablesKt")))
|
||||
@interface MyAppTopLevelCallablesKt : MyAppBase
|
||||
+ (int32_t)topLevelFunction __attribute__((swift_name("topLevelFunction()")));
|
||||
@property (class, readonly) int32_t topLevelProperty __attribute__((swift_name("topLevelProperty")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("TLBMyLibraryB")))
|
||||
@interface MyAppTLBMyLibraryB : MyAppBase
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package org.jetbrains.a
|
||||
|
||||
const val topLevelProperty = 42
|
||||
|
||||
fun topLevelFunction() = 42
|
||||
Reference in New Issue
Block a user