From 10cf97f05b110121f7afb562f3499060a6b30b66 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Fri, 1 Mar 2024 11:34:56 +0100 Subject: [PATCH] [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 --- .../impl/analysis-api/build.gradle.kts | 2 + .../kotlin/objcexport/KtObjCExportFile.kt | 132 ++++++++++++++++++ .../getObjCFileClassOrProtocolName.kt | 5 +- ....kt => translateToObjCExtensionFacades.kt} | 18 +-- .../objcexport/translateToObjCHeader.kt | 114 +++++++-------- ...de.kt => translateToObjCTopLevelFacade.kt} | 8 +- .../kotlin/objcexport/translationOrder.kt | 18 +-- .../testUtils/AnalysisApiHeaderGenerator.kt | 2 +- .../objcexport/tests/TranslationOrderTest.kt | 9 +- ...jCExportDependenciesHeaderGeneratorTest.kt | 5 +- .../!exportedAndNotExportedDependency.h | 7 + .../org/jetbrains/a/TopLevelCallables.kt | 12 ++ 12 files changed, 234 insertions(+), 98 deletions(-) create mode 100644 native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/KtObjCExportFile.kt rename native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/{getExtensionFacades.kt => translateToObjCExtensionFacades.kt} (79%) rename native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/{getTopLevelFacade.kt => translateToObjCTopLevelFacade.kt} (86%) create mode 100644 native/objcexport-header-generator/testDependencies/testLibraryA/src/nativeMain/kotlin/org/jetbrains/a/TopLevelCallables.kt diff --git a/native/objcexport-header-generator/impl/analysis-api/build.gradle.kts b/native/objcexport-header-generator/impl/analysis-api/build.gradle.kts index 6a28f49298b..19ad1cc2db7 100644 --- a/native/objcexport-header-generator/impl/analysis-api/build.gradle.kts +++ b/native/objcexport-header-generator/impl/analysis-api/build.gradle.kts @@ -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")) diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/KtObjCExportFile.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/KtObjCExportFile.kt new file mode 100644 index 00000000000..f775d2c1791 --- /dev/null +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/KtObjCExportFile.kt @@ -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, + val callableSymbols: List, +) + +/* Factory functions */ + +internal fun KtObjCExportFile(file: KtFile): KtObjCExportFile { + return KtPsiObjCExportFile(file) +} + +internal fun createKtObjCExportFiles(addresses: Iterable): List { + 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, +) : 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() + val callableAddresses = addresses.filterIsInstance() + + return KtResolvedObjCExportFile( + fileName = fileName, + packageFqName = packageFqName, + classifierSymbols = classifierAddresses + .mapNotNull { classAddress -> classAddress.getClassOrObjectSymbol() } + .withClosure { symbol -> + symbol.getMemberScope().getClassifierSymbols().filterIsInstance().asIterable() + }.toList(), + callableSymbols = callableAddresses.flatMap { address -> + address.getCallableSymbols() + } + ) + } +} diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCFileClassOrProtocolName.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCFileClassOrProtocolName.kt index 7b22e42a985..52ac1350da8 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCFileClassOrProtocolName.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getObjCFileClassOrProtocolName.kt @@ -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() } \ No newline at end of file diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionFacades.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExtensionFacades.kt similarity index 79% rename from native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionFacades.kt rename to native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExtensionFacades.kt index 3dfb80ad5c3..3c0981ae17a 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionFacades.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCExtensionFacades.kt @@ -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 { - - val extensions = getFileScope() - .getCallableSymbols().filter { it.isExtension } - .toList() +fun KtResolvedObjCExportFile.translateToObjCExtensionFacades(): List { + 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(), diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt index beb3f3a8bea..0fe83c4d577 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCHeader.kt @@ -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): ObjCHeader { val generator = KtObjCExportHeaderGenerator() - generator.translateAll(files.sortedWith(StableFileOrder).map { QueueElement.File(it) }) + + val klibDeclarationAddresses = useSiteModule.closure { module -> module.allDirectDependencies().asIterable() } + .filterIsInstance() + .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): 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() + private val classDeque = ArrayDeque() /** * The mutable aggregate of the already translated elements @@ -68,57 +78,57 @@ private class KtObjCExportHeaderGenerator { */ private val objCClassForwardDeclarations = mutableSetOf() - /** - * 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) { - symbolDeque.addAll(symbolProviders) + fun translateAll(files: List) { + /** + * 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() diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getTopLevelFacade.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCTopLevelFacade.kt similarity index 86% rename from native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getTopLevelFacade.kt rename to native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCTopLevelFacade.kt index 0588edded5d..7ba197d6506 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getTopLevelFacade.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translateToObjCTopLevelFacade.kt @@ -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, diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translationOrder.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translationOrder.kt index b55c79358bd..a1c2011c980 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translationOrder.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/translationOrder.kt @@ -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 - get() = compareBy { file -> file.packageFqName.asString() } - .thenComparing { file -> file.name } +internal val StableFileOrder: Comparator + get() = compareBy { file -> file.packageFqName.asString() } + .thenComparing { file -> file.fileName } internal val StablePropertyOrder: Comparator = compareBy { it.name } @@ -55,17 +54,6 @@ internal val StableCallableOrder: Comparator = compareBy = compareBy { symbol -> - when (symbol) { - is KtFileSymbol -> 0 - is KtClassifierSymbol -> 1 - is KtCallableSymbol -> 2 - else -> Int.MAX_VALUE - } -} - .thenComparing(StableClassifierOrder) - .thenComparing(StableCallableOrder) - private inline fun Comparator.thenComparing(comparator: Comparator): Comparator where R : T { return thenComparing { a, b -> if (a is R && b is R) comparator.compare(a, b) else 0 diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiHeaderGenerator.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiHeaderGenerator.kt index ef34d4f3c69..7b5b5f6bf3a 100644 --- a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiHeaderGenerator.kt +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/testUtils/AnalysisApiHeaderGenerator.kt @@ -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 }) diff --git a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/TranslationOrderTest.kt b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/TranslationOrderTest.kt index a1c6ed337a2..768bcb2daa6 100644 --- a/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/TranslationOrderTest.kt +++ b/native/objcexport-header-generator/impl/analysis-api/test/org/jetbrains/kotlin/objcexport/tests/TranslationOrderTest.kt @@ -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)) diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportDependenciesHeaderGeneratorTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportDependenciesHeaderGeneratorTest.kt index d3869ddfbf9..a2d27378acb 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportDependenciesHeaderGeneratorTest.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportDependenciesHeaderGeneratorTest.kt @@ -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" + ) ) ) } diff --git a/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/!exportedAndNotExportedDependency.h b/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/!exportedAndNotExportedDependency.h index 29b7dd2e60f..df6a7049696 100644 --- a/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/!exportedAndNotExportedDependency.h +++ b/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/!exportedAndNotExportedDependency.h @@ -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 diff --git a/native/objcexport-header-generator/testDependencies/testLibraryA/src/nativeMain/kotlin/org/jetbrains/a/TopLevelCallables.kt b/native/objcexport-header-generator/testDependencies/testLibraryA/src/nativeMain/kotlin/org/jetbrains/a/TopLevelCallables.kt new file mode 100644 index 00000000000..53c89dbc3a0 --- /dev/null +++ b/native/objcexport-header-generator/testDependencies/testLibraryA/src/nativeMain/kotlin/org/jetbrains/a/TopLevelCallables.kt @@ -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 \ No newline at end of file