diff --git a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionsFacade.kt b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionFacades.kt similarity index 57% rename from native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionsFacade.kt rename to native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionFacades.kt index bdd7892a48e..3dfb80ad5c3 100644 --- a/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionsFacade.kt +++ b/native/objcexport-header-generator/impl/analysis-api/src/org/jetbrains/kotlin/objcexport/getExtensionFacades.kt @@ -4,7 +4,6 @@ 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.objcexport.analysisApiUtils.getFileName private const val extensionsCategoryName = "Extensions" @@ -49,26 +48,33 @@ internal val ObjCInterface.isExtensionsFacade: Boolean * See related [getTopLevelFacade] */ context(KtAnalysisSession, KtObjCExportSession) -fun KtFileSymbol.getExtensionsFacade(): ObjCInterface? { +fun KtFileSymbol.getExtensionFacades(): List { val extensions = getFileScope() .getCallableSymbols().filter { it.isExtension } - .toList().sortedWith(StableCallableOrder) - .ifEmpty { return null } + .toList() + .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 + } - val fileName = getFileName() - ?: throw IllegalStateException("File '$this' cannot be translated without file name") - - return ObjCInterfaceImpl( - name = fileName, - comment = null, - origin = null, - attributes = emptyList(), - superProtocols = emptyList(), - members = extensions.mapNotNull { it.translateToObjCExportStub() }, - categoryName = extensionsCategoryName, - generics = emptyList(), - superClass = null, - superClassGenerics = emptyList() - ) + return extensions.map { (objCName, extensionSymbols) -> + ObjCInterfaceImpl( + name = objCName, + comment = null, + origin = null, + attributes = emptyList(), + superProtocols = emptyList(), + members = extensionSymbols.mapNotNull { ext -> ext.translateToObjCExportStub() }, + categoryName = extensionsCategoryName, + generics = emptyList(), + superClass = null, + superClassGenerics = emptyList() + ) + } } \ No newline at end of file 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/getTopLevelFacade.kt index 891b32345a1..0588edded5d 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/getTopLevelFacade.kt @@ -43,7 +43,7 @@ import org.jetbrains.kotlin.objcexport.analysisApiUtils.getDefaultSuperClassOrPr * * Where `FooKt` would be the "top level interface file facade" returned by this function. * - * See related [getExtensionsFacade] + * See related [getExtensionFacades] */ context(KtAnalysisSession, KtObjCExportSession) fun KtFileSymbol.getTopLevelFacade(): ObjCInterface? { 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 eb72e706e38..dfc54508396 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 @@ -109,10 +109,12 @@ private class KtObjCExportHeaderGenerator { context(KtAnalysisSession, KtObjCExportSession) private fun translateFileSymbol(symbol: KtFileSymbol) { - symbol.getExtensionsFacade()?.let { extensionFacade -> - objCStubs += extensionFacade - enqueueDependencyClasses(extensionFacade) - objCClassForwardDeclarations += extensionFacade.name + symbol.getExtensionFacades().let { extensionFacades -> + extensionFacades.forEach { facade -> + objCStubs += facade + enqueueDependencyClasses(facade) + objCClassForwardDeclarations += facade.name + } } symbol.getTopLevelFacade()?.let { topLevelFacade -> @@ -146,10 +148,10 @@ private class KtObjCExportHeaderGenerator { symbol.getDeclaredSuperInterfaceSymbols() .filter { it.isVisibleInObjC() } .forEach { superInterfaceSymbol -> - translateClassOrObjectSymbol(superInterfaceSymbol)?.let { - objCProtocolForwardDeclarations += it.name + translateClassOrObjectSymbol(superInterfaceSymbol)?.let { + objCProtocolForwardDeclarations += it.name + } } - } symbol.getSuperClassSymbolNotAny()?.let { superClassSymbol -> translateClassOrObjectSymbol(superClassSymbol)?.let { diff --git a/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h b/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h index 0af39349a61..2f9d61c29cf 100644 --- a/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h +++ b/native/objcexport-header-generator/testData/headers/extensionFunctions/!extensionFunctions.h @@ -6,7 +6,7 @@ #import #import -@class Foo; +@class ClazzA, ClazzB; NS_ASSUME_NONNULL_BEGIN #pragma clang diagnostic push @@ -21,15 +21,27 @@ NS_ASSUME_NONNULL_BEGIN #endif __attribute__((objc_subclassing_restricted)) -@interface Foo : Base +@interface ClazzA : Base - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); - (void)memberFun __attribute__((swift_name("memberFun()"))); @end -@interface Foo (Extensions) -- (void)extensionFunA __attribute__((swift_name("extensionFunA()"))); -- (void)extensionFunB __attribute__((swift_name("extensionFunB()"))); +__attribute__((objc_subclassing_restricted)) +@interface ClazzB : Base +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (void)memberFun __attribute__((swift_name("memberFun()"))); +@end + +@interface ClazzA (Extensions) +- (void)extensionFunA1 __attribute__((swift_name("extensionFunA1()"))); +- (void)extensionFunA2 __attribute__((swift_name("extensionFunA2()"))); +@end + +@interface ClazzB (Extensions) +- (void)extensionFunB1 __attribute__((swift_name("extensionFunB1()"))); +- (void)extensionFunB2 __attribute__((swift_name("extensionFunB2()"))); @end __attribute__((objc_subclassing_restricted)) diff --git a/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt b/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt index f20146dd380..53ec69e0e3b 100644 --- a/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt +++ b/native/objcexport-header-generator/testData/headers/extensionFunctions/Foo.kt @@ -1,9 +1,16 @@ fun topLevelFunA() {} fun topLevelFunB() {} -fun Foo.extensionFunA() {} -fun Foo.extensionFunB() {} +fun ClazzA.extensionFunA1() {} +fun ClazzA.extensionFunA2() {} -class Foo { +fun ClazzB.extensionFunB1() {} +fun ClazzB.extensionFunB2() {} + +class ClazzA { fun memberFun() {} } + +class ClazzB { + fun memberFun() {} +} \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/headers/extensionProperties/!extensionProperties.h b/native/objcexport-header-generator/testData/headers/extensionProperties/!extensionProperties.h index ba8a01761a4..e672de98fa6 100644 --- a/native/objcexport-header-generator/testData/headers/extensionProperties/!extensionProperties.h +++ b/native/objcexport-header-generator/testData/headers/extensionProperties/!extensionProperties.h @@ -6,7 +6,7 @@ #import #import -@class Foo; +@class Clazz; NS_ASSUME_NONNULL_BEGIN #pragma clang diagnostic push @@ -21,13 +21,13 @@ NS_ASSUME_NONNULL_BEGIN #endif __attribute__((objc_subclassing_restricted)) -@interface Foo : Base +@interface Clazz : Base - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); + (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); - (void)memberFun __attribute__((swift_name("memberFun()"))); @end -@interface Foo (Extensions) +@interface Clazz (Extensions) @property (readonly) int32_t extensionValA __attribute__((swift_name("extensionValA"))); @property (readonly) int32_t extensionValB __attribute__((swift_name("extensionValB"))); @property int32_t extensionVarA __attribute__((swift_name("extensionVarA"))); diff --git a/native/objcexport-header-generator/testData/headers/extensionProperties/Foo.kt b/native/objcexport-header-generator/testData/headers/extensionProperties/Foo.kt index 8d8434f6a65..393cacbf25e 100644 --- a/native/objcexport-header-generator/testData/headers/extensionProperties/Foo.kt +++ b/native/objcexport-header-generator/testData/headers/extensionProperties/Foo.kt @@ -1,18 +1,18 @@ val topLevelPropA = 0 val topLevelPropB = 1 -val Foo.extensionValA +val Clazz.extensionValA get() = 0 -val Foo.extensionValB +val Clazz.extensionValB get() = 1 -var Foo.extensionVarA +var Clazz.extensionVarA get() = 0 set(value) {} -var Foo.extensionVarB +var Clazz.extensionVarB get() = 1 set(value) {} -class Foo { +class Clazz { fun memberFun() {} } \ No newline at end of file