diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt index 191e093f388..da53a74d54d 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt @@ -49,7 +49,8 @@ private interface ObjCContainerImpl { private class ObjCProtocolImpl( name: String, - override val location: Location + override val location: Location, + override val isForwardDeclaration: Boolean ) : ObjCProtocol(name), ObjCContainerImpl { override val protocols = mutableListOf() override val methods = mutableListOf() @@ -58,7 +59,8 @@ private class ObjCProtocolImpl( private class ObjCClassImpl( name: String, - override val location: Location + override val location: Location, + override val isForwardDeclaration: Boolean ) : ObjCClass(name), ObjCContainerImpl { override val protocols = mutableListOf() override val methods = mutableListOf() @@ -285,28 +287,55 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { } } + private fun isObjCInterfaceDeclForward(cursor: CValue): Boolean { + assert(cursor.kind == CXCursorKind.CXCursor_ObjCInterfaceDecl) { cursor.kind } + + // It is forward declaration <=> the first child is reference to it: + var result = false + visitChildren(cursor) { child, _ -> + result = (child.kind == CXCursorKind.CXCursor_ObjCClassRef && clang_getCursorReferenced(child) == cursor) + CXChildVisitResult.CXChildVisit_Break + } + return result + } + private fun getObjCClassAt(cursor: CValue): ObjCClassImpl { assert(cursor.kind == CXCursorKind.CXCursor_ObjCInterfaceDecl) { cursor.kind } val name = clang_getCursorDisplayName(cursor).convertAndDispose() - return objCClassRegistry.getOrPut(cursor, { ObjCClassImpl(name, getLocation(cursor)) }) { + if (isObjCInterfaceDeclForward(cursor)) { + return objCClassRegistry.getOrPut(cursor) { + ObjCClassImpl(name, getLocation(cursor), isForwardDeclaration = true) + } + } + + return objCClassRegistry.getOrPut(cursor, { + ObjCClassImpl(name, getLocation(cursor), isForwardDeclaration = false) + }) { addChildrenToObjCContainer(cursor, it) } } - private fun getObjCProtocolAt(cursor: CValue): ObjCProtocolImpl? { + private fun getObjCProtocolAt(cursor: CValue): ObjCProtocolImpl { assert(cursor.kind == CXCursorKind.CXCursor_ObjCProtocolDecl) { cursor.kind } - if (clang_isCursorDefinition(cursor) == 0) { - val definition = clang_getCursorDefinition(cursor) - if (clang_isCursorDefinition(cursor) == 0) return null - return getObjCProtocolAt(definition) - } - val name = clang_getCursorDisplayName(cursor).convertAndDispose() - return objCProtocolRegistry.getOrPut(cursor, { ObjCProtocolImpl(name, getLocation(cursor)) }) { + if (clang_isCursorDefinition(cursor) == 0) { + val definition = clang_getCursorDefinition(cursor) + return if (clang_isCursorDefinition(definition) != 0) { + getObjCProtocolAt(definition) + } else { + objCProtocolRegistry.getOrPut(cursor) { + ObjCProtocolImpl(name, getLocation(cursor), isForwardDeclaration = true) + } + } + } + + return objCProtocolRegistry.getOrPut(cursor, { + ObjCProtocolImpl(name, getLocation(cursor), isForwardDeclaration = false) + }) { addChildrenToObjCContainer(cursor, it) } } @@ -340,10 +369,9 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { result.baseClass = getObjCClassAt(clang_getCursorReferenced(child)) } CXCursorKind.CXCursor_ObjCProtocolRef -> { - getObjCProtocolAt(clang_getCursorReferenced(child))?.let { - if (it !in result.protocols) { - result.protocols.add(it) - } + val protocol = getObjCProtocolAt(clang_getCursorReferenced(child)) + if (protocol !in result.protocols) { + result.protocols.add(protocol) } } CXCursorKind.CXCursor_ObjCClassMethodDecl, CXCursorKind.CXCursor_ObjCInstanceMethodDecl -> { @@ -576,7 +604,7 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { private fun getProtocols(type: CValue): List { val num = clang_Type_getNumProtocols(type) - return (0 until num).mapNotNull { index -> + return (0 until num).map { index -> getObjCProtocolAt(clang_Type_getProtocol(type, index)) } } @@ -745,7 +773,7 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { val objCContainer: ObjCContainerImpl? = when (container.kind) { CXCursorKind.CXCursor_ObjCCategoryDecl -> getObjCCategoryAt(container) CXCursorKind.CXCursor_ObjCInterfaceDecl -> getObjCClassAt(container) - CXCursorKind.CXCursor_ObjCProtocolDecl -> getObjCProtocolAt(container)!! + CXCursorKind.CXCursor_ObjCProtocolDecl -> getObjCProtocolAt(container) else -> error(container.kind) } diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt index e2cb855d544..4ef788d45eb 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt @@ -136,7 +136,9 @@ sealed class ObjCContainer { abstract val properties: List } -sealed class ObjCClassOrProtocol(val name: String) : ObjCContainer(), TypeDeclaration +sealed class ObjCClassOrProtocol(val name: String) : ObjCContainer(), TypeDeclaration { + abstract val isForwardDeclaration: Boolean +} data class ObjCMethod( val selector: String, val encoding: String, val parameters: List, private val returnType: Type, diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt index d27b1a7e1e2..6d51b46ffe6 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt @@ -29,7 +29,7 @@ fun DeclarationMapper.getKotlinClassFor( objCClassOrProtocol: ObjCClassOrProtocol, isMeta: Boolean = false ): Classifier { - val pkg = if (objCClassOrProtocol.shouldBeImportedAsForwardDeclaration()) { + val pkg = if (objCClassOrProtocol.isForwardDeclaration) { when (objCClassOrProtocol) { is ObjCClass -> "objcnames.classes" is ObjCProtocol -> "objcnames.protocols" diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt index 09c22c277e2..ebc81d9c9b0 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt @@ -506,17 +506,6 @@ fun ObjCClassOrProtocol.kotlinClassName(isMeta: Boolean): String { return if (isMeta) "${baseClassName}Meta" else baseClassName } -fun ObjCClassOrProtocol.shouldBeImportedAsForwardDeclaration(): Boolean { - // Note: currently it is not very easy to distinct class or protocol declarations from forward references, - // so the code below treats the empty declarations as forward ones; - // it is correct because empty and forward declarations feel exactly the same. - - return this.protocols.isEmpty() && - this.methods.isEmpty() && - this.properties.isEmpty() && - (this as? ObjCClass)?.baseClass == null -} - private fun Parameter.getTypeStringRepresentation() = (if (this.nsConsumed) "__attribute__((ns_consumed)) " else "") + type.getStringRepresentation() diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt index c278e500d97..a763e001362 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt @@ -785,13 +785,13 @@ class StubGenerator( stubs.addAll(generateStubsForFunctions(functionsToBind)) nativeIndex.objCProtocols.forEach { - if (!it.shouldBeImportedAsForwardDeclaration()) { + if (!it.isForwardDeclaration) { stubs.add(ObjCProtocolStub(this, it)) } } nativeIndex.objCClasses.forEach { - if (!it.shouldBeImportedAsForwardDeclaration()) { + if (!it.isForwardDeclaration) { stubs.add(ObjCClassStub(this, it)) } } diff --git a/backend.native/tests/interop/objc/smoke.h b/backend.native/tests/interop/objc/smoke.h index d5c43e0fb48..b3ff2028541 100644 --- a/backend.native/tests/interop/objc/smoke.h +++ b/backend.native/tests/interop/objc/smoke.h @@ -1,13 +1,22 @@ #import -@protocol Printer -@required --(void)print:(const char*)string; +@class Foo; + +@protocol Printer; +@protocol Printer; + +@protocol Empty @end; +@protocol Forward; +@class Forward; + +void useForward1(Forward * p) {} +void useForward2(id p) {} + typedef NSString NSStringTypedef; -@interface Foo : NSObject +@interface Foo : NSObject @property NSStringTypedef* name; -(void)helloWithPrinter:(id )printer; @end; @@ -16,6 +25,11 @@ typedef NSString NSStringTypedef; -(void)hello; @end; +@protocol Printer +@required +-(void)print:(const char*)string; +@end; + @protocol MutablePair @required @property (readonly) int first;