[KN] CInterop: __attribute__((objc_direct)) support

Merge-request: KT-MR-8828
Merged-by: Gleb Lukianets <Gleb.Lukianets@jetbrains.com>
This commit is contained in:
Gleb Lukianets
2023-03-02 10:36:53 +00:00
committed by Space Team
parent ae07d0e9ce
commit fc96eb6d8d
17 changed files with 319 additions and 36 deletions
@@ -132,7 +132,7 @@ private class ObjCMethodStubBuilder(
kotlinMethodParameters = method.getKotlinParameters(context, forConstructorOrFactory = false)
external = (container !is ObjCProtocol)
modality = when (container) {
is ObjCClass -> MemberStubModality.OPEN
is ObjCClass -> if (method.isDirect) MemberStubModality.FINAL else MemberStubModality.OPEN
is ObjCProtocol -> if (method.isOptional) MemberStubModality.OPEN else MemberStubModality.ABSTRACT
is ObjCCategory -> MemberStubModality.FINAL
}
@@ -145,7 +145,17 @@ private class ObjCMethodStubBuilder(
private fun buildObjCMethodAnnotations(main: AnnotationStub): List<AnnotationStub> = listOfNotNull(
main,
AnnotationStub.ObjC.ConsumesReceiver.takeIf { method.nsConsumesSelf },
AnnotationStub.ObjC.ReturnsRetained.takeIf { method.nsReturnsRetained }
AnnotationStub.ObjC.ReturnsRetained.takeIf { method.nsReturnsRetained },
if (method.isDirect) {
when (container) {
is ObjCClass -> container.name
is ObjCCategory -> container.clazz.name
is ObjCProtocol -> null
}?.let {
val prefix = if (method.isClass) '+' else '-'
AnnotationStub.ObjC.Direct("$prefix[$it ${method.selector}]")
}
} else { null },
)
fun isDefaultConstructor(): Boolean =
@@ -185,6 +185,9 @@ sealed class AnnotationStub(val classifier: Classifier) {
class Method(val selector: String, val encoding: String, val isStret: Boolean = false) :
ObjC(Classifier.topLevel(cinteropPackage, "ObjCMethod"))
class Direct(val symbol: String) :
ObjC(Classifier.topLevel(cinteropPackage, "ObjCDirect"))
class Factory(val selector: String, val encoding: String, val isStret: Boolean = false) :
ObjC(Classifier.topLevel(cinteropPackage, "ObjCFactory"))
@@ -394,6 +394,9 @@ private class MappingExtensions(
("encoding" to encoding).asAnnotationArgument(),
("isStret" to KmAnnotationArgument.BooleanValue(isStret))
)
is AnnotationStub.ObjC.Direct -> mapOfNotNull(
("symbol" to symbol).asAnnotationArgument(),
)
is AnnotationStub.ObjC.Factory -> mapOfNotNull(
("selector" to selector).asAnnotationArgument(),
("encoding" to encoding).asAnnotationArgument(),
@@ -458,6 +458,7 @@ class StubIrTextEmitter(
val encoding = annotationStub.encoding.quoteAsKotlinLiteral()
"@ObjCMethod($selector, $encoding$stret)"
}
is AnnotationStub.ObjC.Direct -> "@ObjCDirect(${annotationStub.symbol.quoteAsKotlinLiteral()})"
is AnnotationStub.ObjC.Factory -> {
val stret = if (annotationStub.isStret) ", true" else ""
val selector = annotationStub.selector.quoteAsKotlinLiteral()
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2022 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.native.interop.gen
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.native.interop.indexer.buildNativeIndex
import org.junit.Assume
import org.junit.BeforeClass
import org.junit.Test
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class ObjCMethodSignaturesTest : InteropTestsBase() {
companion object {
@BeforeClass
@JvmStatic
fun assumeMacOS() {
Assume.assumeTrue(HostManager.hostIsMac)
}
}
@Test
fun `ObjC Direct`() {
val files = TempFiles("ObjCDirect")
files.file("header.h", """
#import <Foundation/Foundation.h>
@interface Foo : NSObject
+ (void)direct __attribute__((objc_direct));
- (void)direct __attribute__((objc_direct));
@end
@interface Foo(Ext)
+ (void)directExt __attribute__((objc_direct));
- (void)directExt __attribute__((objc_direct));
@end
""".trimIndent())
val defFile = files.file("direct.def", """
language = Objective-C
headers = header.h
""".trimIndent())
val library = buildNativeLibraryFrom(defFile, files.directory)
val index = buildNativeIndex(library, false).index
index.objCClasses.find { it.name == "Foo" }.let { cls ->
assertNotNull(cls, "Class 'Foo' not found in native library $library")
assertNotNull(cls.methods.find { it.selector == "direct" && it.isClass && it.isDirect })
assertNotNull(cls.methods.find { it.selector == "direct" && !it.isClass && it.isDirect })
}
index.objCCategories.find { it.name == "Ext" && it.clazz.name == "Foo" }.let { cat ->
assertNotNull(cat, "Category 'Foo(Ext)' not found in native library $library")
assertNotNull(cat.methods.find { it.selector == "directExt" && it.isClass && it.isDirect })
assertNotNull(cat.methods.find { it.selector == "directExt" && !it.isClass && it.isDirect })
}
}
}