[KN] CInterop: __attribute__((objc_direct)) support
Merge-request: KT-MR-8828 Merged-by: Gleb Lukianets <Gleb.Lukianets@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
ae07d0e9ce
commit
fc96eb6d8d
+11
-9
@@ -1150,14 +1150,15 @@ public open class NativeIndexImpl(val library: NativeLibrary, val verbose: Boole
|
||||
}
|
||||
|
||||
return ObjCMethod(
|
||||
selector, encoding, parameters, returnType,
|
||||
isVariadic = clang_Cursor_isVariadic(cursor) != 0,
|
||||
isClass = isClass,
|
||||
nsConsumesSelf = clang_Cursor_isObjCConsumingSelfMethod(cursor) != 0,
|
||||
nsReturnsRetained = clang_Cursor_isObjCReturningRetainedMethod(cursor) != 0,
|
||||
isOptional = (clang_Cursor_isObjCOptional(cursor) != 0),
|
||||
isInit = (clang_Cursor_isObjCInitMethod(cursor) != 0),
|
||||
isExplicitlyDesignatedInitializer = hasAttribute(cursor, OBJC_DESGINATED_INITIALIZER)
|
||||
selector, encoding, parameters, returnType,
|
||||
isVariadic = clang_Cursor_isVariadic(cursor) != 0,
|
||||
isClass = isClass,
|
||||
nsConsumesSelf = clang_Cursor_isObjCConsumingSelfMethod(cursor) != 0,
|
||||
nsReturnsRetained = clang_Cursor_isObjCReturningRetainedMethod(cursor) != 0,
|
||||
isOptional = (clang_Cursor_isObjCOptional(cursor) != 0),
|
||||
isInit = (clang_Cursor_isObjCInitMethod(cursor) != 0),
|
||||
isExplicitlyDesignatedInitializer = hasAttribute(cursor, OBJC_DESIGNATED_INITIALIZER),
|
||||
isDirect = hasAttribute(cursor, OBJC_DIRECT),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1201,7 +1202,8 @@ public open class NativeIndexImpl(val library: NativeLibrary, val verbose: Boole
|
||||
}
|
||||
|
||||
private val NS_CONSUMED = "ns_consumed"
|
||||
private val OBJC_DESGINATED_INITIALIZER = "objc_designated_initializer"
|
||||
private val OBJC_DESIGNATED_INITIALIZER = "objc_designated_initializer"
|
||||
private val OBJC_DIRECT = "objc_direct"
|
||||
|
||||
private fun hasAttribute(cursor: CValue<CXCursor>, name: String): Boolean {
|
||||
var result = false
|
||||
|
||||
+1
-1
@@ -257,7 +257,7 @@ sealed class ObjCClassOrProtocol(val name: String) : ObjCContainer(), TypeDeclar
|
||||
data class ObjCMethod(
|
||||
val selector: String, val encoding: String, val parameters: List<Parameter>, private val returnType: Type,
|
||||
val isVariadic: Boolean, val isClass: Boolean, val nsConsumesSelf: Boolean, val nsReturnsRetained: Boolean,
|
||||
val isOptional: Boolean, val isInit: Boolean, val isExplicitlyDesignatedInitializer: Boolean
|
||||
val isOptional: Boolean, val isInit: Boolean, val isExplicitlyDesignatedInitializer: Boolean, val isDirect: Boolean
|
||||
) {
|
||||
|
||||
fun returnsInstancetype(): Boolean = returnType is ObjCInstanceType
|
||||
|
||||
@@ -112,6 +112,10 @@ annotation class ExternalObjCClass(val protocolGetter: String = "", val binaryNa
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ObjCMethod(val selector: String, val encoding: String, val isStret: Boolean = false)
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ObjCDirect(val symbol: String)
|
||||
|
||||
@Target(AnnotationTarget.CONSTRUCTOR)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ObjCConstructor(val initSelector: String, val designated: Boolean)
|
||||
|
||||
+12
-2
@@ -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 =
|
||||
|
||||
+3
@@ -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"))
|
||||
|
||||
|
||||
+3
@@ -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(),
|
||||
|
||||
+1
@@ -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()
|
||||
|
||||
+60
@@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-7
@@ -37,6 +37,7 @@ private val objCProtocolFqName = interopPackageName.child(Name.identifier("ObjCP
|
||||
private val objCProtocolIdSignature = getTopLevelPublicSignature(objCProtocolFqName)
|
||||
internal val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
|
||||
private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod"))
|
||||
private val objCDirectFqName = interopPackageName.child(Name.identifier("ObjCDirect"))
|
||||
private val objCConstructorFqName = FqName("kotlinx.cinterop.ObjCConstructor")
|
||||
private val objCFactoryFqName = interopPackageName.child(Name.identifier("ObjCFactory"))
|
||||
private val objcnamesForwardDeclarationsPackageName = Name.identifier("objcnames")
|
||||
@@ -111,30 +112,53 @@ fun IrClass.isKotlinObjCClass(): Boolean = this.isObjCClass() && !this.isExterna
|
||||
|
||||
data class ObjCMethodInfo(val selector: String,
|
||||
val encoding: String,
|
||||
val isStret: Boolean)
|
||||
val isStret: Boolean,
|
||||
val directSymbol: String?)
|
||||
|
||||
private fun FunctionDescriptor.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
|
||||
assert (this.kind.isReal)
|
||||
val methodAnnotation = this.annotations.findAnnotation(objCMethodFqName) ?: return null
|
||||
return objCMethodInfo(methodAnnotation)
|
||||
|
||||
val methodInfo = this.annotations.findAnnotation(objCMethodFqName)?.let {
|
||||
ObjCMethodInfo(
|
||||
selector = it.getStringValue("selector"),
|
||||
encoding = it.getStringValue("encoding"),
|
||||
isStret = it.getArgumentValueOrNull<Boolean>("isStret") ?: false,
|
||||
directSymbol = this.annotations.findAnnotation(objCDirectFqName)?.let {
|
||||
it.getStringValue("symbol")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return methodInfo
|
||||
}
|
||||
|
||||
private fun IrFunction.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
|
||||
assert (this.isReal)
|
||||
val methodAnnotation = this.annotations.findAnnotation(objCMethodFqName) ?: return null
|
||||
return objCMethodInfo(methodAnnotation)
|
||||
|
||||
val methodInfo = this.annotations.findAnnotation(objCMethodFqName)?.let {
|
||||
ObjCMethodInfo(
|
||||
selector = it.getAnnotationStringValue("selector"),
|
||||
encoding = it.getAnnotationStringValue("encoding"),
|
||||
isStret = it.getAnnotationValueOrNull<Boolean>("isStret") ?: false,
|
||||
directSymbol = this.annotations.findAnnotation(objCDirectFqName)?.getAnnotationStringValue("symbol"),
|
||||
)
|
||||
}
|
||||
|
||||
return methodInfo
|
||||
}
|
||||
|
||||
private fun objCMethodInfo(annotation: AnnotationDescriptor) = ObjCMethodInfo(
|
||||
selector = annotation.getStringValue("selector"),
|
||||
encoding = annotation.getStringValue("encoding"),
|
||||
isStret = annotation.getArgumentValueOrNull<Boolean>("isStret") ?: false
|
||||
isStret = annotation.getArgumentValueOrNull<Boolean>("isStret") ?: false,
|
||||
directSymbol = null,
|
||||
)
|
||||
|
||||
private fun objCMethodInfo(annotation: IrConstructorCall) = ObjCMethodInfo(
|
||||
selector = annotation.getAnnotationStringValue("selector"),
|
||||
encoding = annotation.getAnnotationStringValue("encoding"),
|
||||
isStret = annotation.getAnnotationValueOrNull<Boolean>("isStret") ?: false
|
||||
isStret = annotation.getAnnotationValueOrNull<Boolean>("isStret") ?: false,
|
||||
directSymbol = null,
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
+37
-17
@@ -307,12 +307,15 @@ internal fun KotlinStubs.generateObjCCall(
|
||||
method: IrSimpleFunction,
|
||||
isStret: Boolean,
|
||||
selector: String,
|
||||
directSymbolName: String?,
|
||||
call: IrFunctionAccessExpression,
|
||||
superQualifier: IrClassSymbol?,
|
||||
receiver: ObjCCallReceiver,
|
||||
arguments: List<IrExpression?>
|
||||
) = builder.irBlock {
|
||||
val resolved = method.resolveFakeOverride(allowAbstract = true)?: method
|
||||
val isDirect = directSymbolName != null
|
||||
|
||||
val exceptionMode = ForeignExceptionMode.byValue(
|
||||
resolved.konanLibrary?.manifestProperties
|
||||
?.getProperty(ForeignExceptionMode.manifestKey)
|
||||
@@ -325,20 +328,23 @@ internal fun KotlinStubs.generateObjCCall(
|
||||
isMutable = true
|
||||
)
|
||||
|
||||
val messenger = irCall(if (isStret) {
|
||||
symbols.interopGetMessengerStret
|
||||
} else {
|
||||
symbols.interopGetMessenger
|
||||
}.owner).apply {
|
||||
putValueArgument(0, irGet(superClass)) // TODO: check superClass statically.
|
||||
}
|
||||
val targetPtrParameter = if (!isDirect) {
|
||||
val messenger = irCall(if (isStret) {
|
||||
symbols.interopGetMessengerStret
|
||||
} else {
|
||||
symbols.interopGetMessenger
|
||||
}.owner).apply {
|
||||
putValueArgument(0, irGet(superClass)) // TODO: check superClass statically.
|
||||
}
|
||||
|
||||
val targetPtrParameter = callBuilder.passThroughBridge(
|
||||
messenger,
|
||||
symbols.interopCPointer.starProjectedType,
|
||||
CTypes.voidPtr
|
||||
).name
|
||||
val targetFunctionName = "targetPtr"
|
||||
callBuilder.passThroughBridge(
|
||||
messenger,
|
||||
symbols.interopCPointer.starProjectedType,
|
||||
CTypes.voidPtr
|
||||
).name
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val preparedReceiver = if (method.objCConsumesReceiver()) {
|
||||
when (receiver) {
|
||||
@@ -381,17 +387,31 @@ internal fun KotlinStubs.generateObjCCall(
|
||||
receiverOrSuper, symbols.nativePtrType, CTypes.voidPtr).name
|
||||
callBuilder.cFunctionBuilder.addParameter(CTypes.voidPtr)
|
||||
|
||||
callBuilder.cCallBuilder.arguments += "@selector($selector)"
|
||||
callBuilder.cFunctionBuilder.addParameter(CTypes.voidPtr)
|
||||
if (isDirect) {
|
||||
callBuilder.cCallBuilder.arguments += "0"
|
||||
callBuilder.cFunctionBuilder.addParameter(CTypes.voidPtr)
|
||||
} else {
|
||||
callBuilder.cCallBuilder.arguments += "@selector($selector)"
|
||||
callBuilder.cFunctionBuilder.addParameter(CTypes.voidPtr)
|
||||
}
|
||||
|
||||
callBuilder.addArguments(arguments, method)
|
||||
|
||||
val returnValuePassing = mapReturnType(method.returnType, call, signature = method)
|
||||
|
||||
val targetFunctionName = getUniqueCName("knbridge_targetPtr")
|
||||
|
||||
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
|
||||
|
||||
val targetFunctionVariable = CVariable(CTypes.pointer(callBuilder.cFunctionBuilder.getType()), targetFunctionName)
|
||||
callBuilder.cBridgeBodyLines.add(0, "$targetFunctionVariable = $targetPtrParameter;")
|
||||
if (isDirect) {
|
||||
// This declares a function
|
||||
val targetFunctionVariable = CVariable(callBuilder.cFunctionBuilder.getType(), targetFunctionName)
|
||||
callBuilder.cBridgeBodyLines.add(0, "$targetFunctionVariable __asm(\"$directSymbolName\");")
|
||||
|
||||
} else {
|
||||
val targetFunctionVariable = CVariable(CTypes.pointer(callBuilder.cFunctionBuilder.getType()), targetFunctionName)
|
||||
callBuilder.cBridgeBodyLines.add(0, "$targetFunctionVariable = $targetPtrParameter;")
|
||||
}
|
||||
|
||||
callBuilder.emitCBridge()
|
||||
|
||||
|
||||
+2
@@ -551,6 +551,7 @@ private class InteropLoweringPart1(val generationState: NativeGenerationState) :
|
||||
method,
|
||||
info.isStret,
|
||||
info.selector,
|
||||
info.directSymbol,
|
||||
call,
|
||||
superQualifier,
|
||||
receiver,
|
||||
@@ -626,6 +627,7 @@ private class InteropLoweringPart1(val generationState: NativeGenerationState) :
|
||||
require(expression.superQualifierSymbol?.owner?.isInterface != true) { renderCompilerError(expression) }
|
||||
|
||||
builder.at(expression)
|
||||
|
||||
return builder.genLoweredObjCMethodCall(
|
||||
methodInfo,
|
||||
superQualifier = expression.superQualifierSymbol,
|
||||
|
||||
@@ -4379,6 +4379,13 @@ if (PlatformInfo.isAppleTarget(project)) {
|
||||
it.headers "$projectDir/interop/objc/msg_send/messaging.h"
|
||||
}
|
||||
|
||||
createInterop("objcDirect") {
|
||||
it.defFile 'interop/objc/direct/direct.def'
|
||||
it.headers "$projectDir/interop/objc/direct/direct.h"
|
||||
it.extraOpts "-Xcompile-source", "$projectDir/interop/objc/direct/direct.m", "-Xsource-compiler-option", "-DNS_FORMAT_ARGUMENT(A)="
|
||||
it.compilerOpts '-DNS_FORMAT_ARGUMENT(A)='
|
||||
}
|
||||
|
||||
createInterop("foreignException") {
|
||||
it.defFile 'interop/objc/foreignException/objc_wrap.def'
|
||||
it.headers "$projectDir/interop/objc/foreignException/objc_wrap.h"
|
||||
@@ -5081,6 +5088,11 @@ if (PlatformInfo.isAppleTarget(project)) {
|
||||
}
|
||||
}
|
||||
|
||||
interopTest("interop_objc_direct") {
|
||||
source = "interop/objc/direct/main.kt"
|
||||
interop = "objcDirect"
|
||||
}
|
||||
|
||||
interopTest("interop_objc_foreignException") {
|
||||
disabled = isK2(project) // KT-55909
|
||||
source = "interop/objc/foreignException/objc_wrap.kt"
|
||||
@@ -6583,6 +6595,15 @@ fileCheckTest("filecheck_constants_merge") {
|
||||
annotatedSource = project.file('filecheck/constants_merge.kt')
|
||||
}
|
||||
|
||||
|
||||
fileCheckTest("filecheck_objc_direct") {
|
||||
enabled = enabled && cacheTesting == null
|
||||
annotatedSource = project.file('filecheck/objc_direct.kt')
|
||||
interop = "objcDirect"
|
||||
enabled = target.family.appleFamily
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
nopPluginApi kotlinCompilerModule
|
||||
nopPluginApi project(":native:kotlin-native-utils")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.
|
||||
*/
|
||||
|
||||
import direct.*
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
//CHECK-LABEL: kfun:#callDirect(){}kotlin.ULong
|
||||
fun callDirect(): ULong {
|
||||
val cc = CallingConventions()
|
||||
//CHECK: invoke i64 @_{{[a-zA-Z0-9]+}}_knbridge{{[0-9]+}}(i8* %{{[0-9]+}}, i64 42)
|
||||
return cc.direct(42)
|
||||
}
|
||||
|
||||
//CHECK-LABEL: kfun:#callRegular(){}kotlin.ULong
|
||||
fun callRegular(): ULong {
|
||||
val cc = CallingConventions()
|
||||
//CHECK: invoke i64 @_{{[a-zA-Z0-9]+}}_knbridge{{[0-9]+}}(i8* %{{[0-9]+}}, i8* %{{[0-9]+}}, i64 42)
|
||||
return cc.regular(42)
|
||||
}
|
||||
|
||||
fun main() {
|
||||
callDirect()
|
||||
callRegular()
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
language = Objective-C
|
||||
headerFilter = **/direct.h
|
||||
@@ -0,0 +1,32 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
__attribute__((objc_runtime_name("CC")))
|
||||
__attribute__((swift_name("CC")))
|
||||
@interface CallingConventions : NSObject
|
||||
|
||||
+ (NSUInteger)regular:(NSUInteger)arg;
|
||||
- (NSUInteger)regular:(NSUInteger)arg;
|
||||
|
||||
+ (NSUInteger)direct:(NSUInteger)arg __attribute__((objc_direct));
|
||||
- (NSUInteger)direct:(NSUInteger)arg __attribute__((objc_direct));
|
||||
|
||||
@end
|
||||
|
||||
@interface CallingConventions(Ext)
|
||||
|
||||
+ (NSUInteger)regularExt:(NSUInteger)arg;
|
||||
- (NSUInteger)regularExt:(NSUInteger)arg;
|
||||
|
||||
+ (NSUInteger)directExt:(NSUInteger)arg __attribute__((objc_direct));
|
||||
- (NSUInteger)directExt:(NSUInteger)arg __attribute__((objc_direct));
|
||||
|
||||
@end
|
||||
|
||||
__attribute__((objc_runtime_name("CCH")))
|
||||
__attribute__((swift_name("CCH")))
|
||||
@interface CallingConventionsHeir : CallingConventions
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,27 @@
|
||||
#import "direct.h"
|
||||
|
||||
#define TEST_METHOD_IMPL(NAME) (NSUInteger)NAME:(NSUInteger)arg { return arg; }
|
||||
|
||||
@implementation CallingConventions : NSObject
|
||||
|
||||
+ TEST_METHOD_IMPL(regular);
|
||||
- TEST_METHOD_IMPL(regular);
|
||||
|
||||
+ TEST_METHOD_IMPL(direct);
|
||||
- TEST_METHOD_IMPL(direct);
|
||||
|
||||
@end
|
||||
|
||||
@implementation CallingConventions(Ext)
|
||||
|
||||
+ TEST_METHOD_IMPL(regularExt);
|
||||
- TEST_METHOD_IMPL(regularExt);
|
||||
|
||||
+ TEST_METHOD_IMPL(directExt);
|
||||
- TEST_METHOD_IMPL(directExt);
|
||||
|
||||
@end
|
||||
|
||||
@implementation CallingConventionsHeir
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
import direct.*
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
|
||||
class CallingConventionsNativeHeir() : CallingConventions() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
typealias CC = CallingConventions
|
||||
typealias CCH = CallingConventionsHeir
|
||||
typealias CCN = CallingConventionsNativeHeir
|
||||
|
||||
// KT-54610
|
||||
fun main(args: Array<String>) {
|
||||
autoreleasepool {
|
||||
val cc = CC()
|
||||
val cch = CCH()
|
||||
val ccn = CCN()
|
||||
|
||||
assertEquals(42UL, CC.regular(42))
|
||||
assertEquals(42UL, cc.regular(42))
|
||||
assertEquals(42UL, CC.regularExt(42))
|
||||
assertEquals(42UL, cc.regularExt(42))
|
||||
|
||||
assertEquals(42UL, CCH.regular(42))
|
||||
assertEquals(42UL, cch.regular(42))
|
||||
assertEquals(42UL, CCH.regularExt(42))
|
||||
assertEquals(42UL, cch.regularExt(42))
|
||||
|
||||
assertEquals(42UL, ccn.regular(42UL))
|
||||
assertEquals(42UL, ccn.regularExt(42UL))
|
||||
|
||||
assertEquals(42UL, CC.direct(42))
|
||||
assertEquals(42UL, cc.direct(42))
|
||||
assertEquals(42UL, CC.directExt(42))
|
||||
assertEquals(42UL, cc.directExt(42))
|
||||
|
||||
assertEquals(42UL, CCH.direct(42))
|
||||
assertEquals(42UL, cch .direct(42))
|
||||
assertEquals(42UL, CCH.directExt(42))
|
||||
assertEquals(42UL, cch .directExt(42))
|
||||
|
||||
assertEquals(42UL, ccn .direct(42UL))
|
||||
assertEquals(42UL, ccn .directExt(42UL))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user