Rework interop callbacks
* Do not rely on type inference and `CFunctionType`;
* Represent struct-typed parameters and return values as `CValue<*>`
(currently supported only on JVM).
This commit is contained in:
committed by
SvyatoslavScherbina
parent
cc73c7e009
commit
3f84ba462f
@@ -16,6 +16,15 @@
|
||||
|
||||
package kotlinx.cinterop
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.companionObjectInstance
|
||||
import kotlin.reflect.full.declaredMemberProperties
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.jvm.reflect
|
||||
|
||||
/**
|
||||
* This class provides a way to create a stable handle to any Kotlin object.
|
||||
* Its [value] can be safely passed to native code e.g. to be received in a Kotlin callback.
|
||||
@@ -59,112 +68,348 @@ data class StableObjPtr private constructor(val value: COpaquePointer) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the type of C function with adapter for Kotlin functions.
|
||||
*
|
||||
* The instances of this class are supposed to be Kotlin object declarations (singletons),
|
||||
* because it is required by [CAdaptedFunctionType] and
|
||||
* because creating the instance implies allocating some amount of non-freeable memory for the instance itself
|
||||
* and for any unique Kotlin function "converted" to this type.
|
||||
*
|
||||
* Native function type definition consists in the following:
|
||||
* - Definitions of native function's parameter and return types to be passed into the constructor
|
||||
* - Implementation of [invoke] method which describes how to convert between these types and Kotlin types used in [F]
|
||||
*
|
||||
* @param F Kotlin function type corresponding to given native function type
|
||||
*/
|
||||
abstract class CAdaptedFunctionTypeImpl<F : Function<*>>
|
||||
protected constructor(returnType: CType, vararg paramTypes: CType) : CAdaptedFunctionType<F> {
|
||||
|
||||
override fun fromStatic(function: F): NativePtr {
|
||||
// TODO: optimize synchronization
|
||||
synchronized(cache) {
|
||||
return cache.getOrPut(function, { createFromStatic(function) })
|
||||
}
|
||||
private fun getFieldCType(type: KType): CType<*> {
|
||||
val classifier = type.classifier
|
||||
if (classifier is KClass<*> && classifier.isSubclassOf(CStructVar::class)) {
|
||||
return getStructCType(classifier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the C type of a function's parameter or return value.
|
||||
* It is supposed to be constructed using the primitive types (such as [SInt32]) and the [Struct] combinator.
|
||||
*
|
||||
* This description omits the details that are irrelevant for the ABI.
|
||||
*/
|
||||
protected open class CType internal constructor(val ffiType: ffi_type) {
|
||||
internal constructor(ffiTypePtr: Long) : this(interpretPointed<ffi_type>(ffiTypePtr))
|
||||
}
|
||||
|
||||
protected object Void : CType(ffiTypeVoid())
|
||||
protected object UInt8 : CType(ffiTypeUInt8())
|
||||
protected object SInt8 : CType(ffiTypeSInt8())
|
||||
protected object UInt16 : CType(ffiTypeUInt16())
|
||||
protected object SInt16 : CType(ffiTypeSInt16())
|
||||
protected object UInt32 : CType(ffiTypeUInt32())
|
||||
protected object SInt32 : CType(ffiTypeSInt32())
|
||||
protected object UInt64 : CType(ffiTypeUInt64())
|
||||
protected object SInt64 : CType(ffiTypeSInt64())
|
||||
protected object Pointer : CType(ffiTypePointer())
|
||||
|
||||
protected class Struct(vararg elementTypes: CType) : CType(
|
||||
ffiTypeStruct(
|
||||
elementTypes.map { it.ffiType }
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* This method should invoke given Kotlin function.
|
||||
*
|
||||
* @param args array of pointers to arguments to be passed to [function]
|
||||
* @param ret pointer to memory to be filled with return value of [function]
|
||||
*/
|
||||
protected abstract fun invoke(function: F, args: CArrayPointer<COpaquePointerVar>, ret: COpaquePointer)
|
||||
|
||||
companion object {
|
||||
init {
|
||||
loadCallbacksLibrary()
|
||||
}
|
||||
}
|
||||
|
||||
private val ffiCif = ffiCreateCif(returnType.ffiType, paramTypes.map { it.ffiType })
|
||||
|
||||
/**
|
||||
* Allocates a native function of this type for given Kotlin function.
|
||||
*/
|
||||
private fun createFromStatic(function: F): NativePtr {
|
||||
if (!isStatic(function)) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
val impl: UserData = { ret: COpaquePointer, args: CArrayPointer<COpaquePointerVar> ->
|
||||
invoke(function, args, ret)
|
||||
}
|
||||
|
||||
return ffiCreateClosure(ffiCif, impl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if given function is *static* as defined in [fromStatic].
|
||||
*/
|
||||
private fun isStatic(function: Function<*>): Boolean {
|
||||
// TODO: revise
|
||||
try {
|
||||
with(function.javaClass.getDeclaredField("INSTANCE")) {
|
||||
if (!java.lang.reflect.Modifier.isStatic(modifiers) || !java.lang.reflect.Modifier.isFinal(modifiers)) {
|
||||
return false
|
||||
}
|
||||
|
||||
isAccessible = true // TODO: undo
|
||||
|
||||
return get(null) == function
|
||||
}
|
||||
} catch (e: NoSuchFieldException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private val cache = mutableMapOf<F, NativePtr>()
|
||||
return getArgOrRetValCType(type)
|
||||
}
|
||||
|
||||
private typealias UserData = (ret: COpaquePointer, args: CArrayPointer<COpaquePointerVar>)->Unit
|
||||
private fun getVariableCType(type: KType): CType<*>? {
|
||||
val classifier = type.classifier
|
||||
return when (classifier) {
|
||||
!is KClass<*> -> null
|
||||
ByteVarOf::class -> SInt8
|
||||
ShortVarOf::class -> SInt16
|
||||
IntVarOf::class -> SInt32
|
||||
LongVarOf::class -> SInt64
|
||||
CPointerVarOf::class -> Pointer
|
||||
// TODO: floats, enums.
|
||||
else -> if (classifier.isSubclassOf(CStructVar::class)) {
|
||||
getStructCType(classifier)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val structTypeCache = ConcurrentHashMap<Class<*>, CType<*>>()
|
||||
|
||||
private fun getStructCType(structClass: KClass<*>): CType<*> = structTypeCache.computeIfAbsent(structClass.java) {
|
||||
// Note that struct classes are not supposed to be user-defined,
|
||||
// so they don't require to be checked strictly.
|
||||
|
||||
val annotations = structClass.annotations
|
||||
val cNaturalStruct = annotations.filterIsInstance<CNaturalStruct>().firstOrNull() ?:
|
||||
error("struct ${structClass.simpleName} has custom layout")
|
||||
|
||||
val propertiesByName = structClass.declaredMemberProperties.groupBy { it.name }
|
||||
|
||||
val fields = cNaturalStruct.fieldNames.map {
|
||||
propertiesByName[it]!!.single()
|
||||
}
|
||||
|
||||
val fieldCTypes = mutableListOf<CType<*>>()
|
||||
|
||||
for (field in fields) {
|
||||
val lengthAnnotation = field.annotations.filterIsInstance<CLength>().firstOrNull()
|
||||
if (lengthAnnotation == null) {
|
||||
val fieldType = getFieldCType(field.returnType)
|
||||
fieldCTypes.add(fieldType)
|
||||
} else {
|
||||
assert(field.returnType.classifier == CPointer::class)
|
||||
val length = lengthAnnotation.value
|
||||
if (length != 0) {
|
||||
val pointed = field.returnType.arguments.single().type!!
|
||||
val pointedCType = getVariableCType(pointed) ?: TODO("array element type '$pointed'")
|
||||
|
||||
// Represent array field as repeated element-typed fields:
|
||||
repeat(length) {
|
||||
fieldCTypes.add(pointedCType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val structType = structClass.companionObjectInstance as CVariable.Type
|
||||
|
||||
Struct(structType.size, structType.align, fieldCTypes)
|
||||
}
|
||||
|
||||
private fun getStructValueCType(type: KType): CType<*> {
|
||||
val structClass = type.arguments.singleOrNull()?.type?.classifier as? KClass<*> ?:
|
||||
error("'$type' type is incomplete")
|
||||
|
||||
return getStructCType(structClass)
|
||||
}
|
||||
|
||||
private fun getEnumCType(classifier: KClass<*>): CEnumType? {
|
||||
val rawValueType = classifier.declaredMemberProperties.single().returnType
|
||||
|
||||
val rawValueCType = when (rawValueType.classifier) {
|
||||
Byte::class -> SInt8
|
||||
Short::class -> SInt16
|
||||
Int::class -> SInt32
|
||||
Long::class -> SInt64
|
||||
else -> error("'${classifier.simpleName}' has unexpected value type '$rawValueType'")
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return CEnumType(rawValueCType as CType<Number>)
|
||||
}
|
||||
|
||||
private fun getArgOrRetValCType(type: KType): CType<*> {
|
||||
val classifier = type.classifier
|
||||
|
||||
val result = when (classifier) {
|
||||
!is KClass<*> -> null
|
||||
Unit::class -> Void
|
||||
Byte::class -> SInt8
|
||||
Short::class -> SInt16
|
||||
Int::class -> SInt32
|
||||
Long::class -> SInt64
|
||||
CPointer::class -> Pointer
|
||||
// TODO: floats
|
||||
CValue::class -> getStructValueCType(type)
|
||||
else -> if (classifier.isSubclassOf(CEnum::class)) {
|
||||
getEnumCType(classifier)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} ?: error("$type is not supported in callback signature")
|
||||
|
||||
if (type.isMarkedNullable != (classifier == CPointer::class)) {
|
||||
if (type.isMarkedNullable) {
|
||||
error("$type must not be nullable when used in callback signature")
|
||||
} else {
|
||||
error("$type must be nullable when used in callback signature")
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun createStaticCFunction(function: Function<*>): CPointer<CFunction<*>> {
|
||||
val errorMessage = "staticCFunction must take an unbound, non-capturing function"
|
||||
|
||||
if (!isStatic(function)) {
|
||||
throw IllegalArgumentException(errorMessage)
|
||||
}
|
||||
|
||||
val kFunction = function as? KFunction<*> ?: function.reflect() ?:
|
||||
throw IllegalArgumentException(errorMessage)
|
||||
|
||||
val returnType = getArgOrRetValCType(kFunction.returnType)
|
||||
val paramTypes = kFunction.parameters.map { getArgOrRetValCType(it.type) }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return interpretCPointer(createStaticCFunctionImpl(returnType as CType<Any?>, paramTypes, function))!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if given function is *static* as defined in [staticCFunction].
|
||||
*/
|
||||
private fun isStatic(function: Function<*>): Boolean {
|
||||
// TODO: revise
|
||||
try {
|
||||
with(function.javaClass.getDeclaredField("INSTANCE")) {
|
||||
if (!java.lang.reflect.Modifier.isStatic(modifiers) || !java.lang.reflect.Modifier.isFinal(modifiers)) {
|
||||
return false
|
||||
}
|
||||
|
||||
isAccessible = true // TODO: undo
|
||||
|
||||
return get(null) == function
|
||||
|
||||
// If the class has static final "INSTANCE" field, and only the value of this field is accepted,
|
||||
// then each class is handled at most once, so these checks prevent memory leaks.
|
||||
}
|
||||
} catch (e: NoSuchFieldException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private val createdStaticFunctions = ConcurrentHashMap<Class<*>, CPointer<CFunction<*>>>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <F : Function<*>> staticCFunctionImpl(function: F) =
|
||||
createdStaticFunctions.computeIfAbsent(function.javaClass) {
|
||||
createStaticCFunction(function)
|
||||
} as CPointer<CFunction<F>>
|
||||
|
||||
private val invokeMethods = (0 .. 22).map { arity ->
|
||||
Class.forName("kotlin.jvm.functions.Function$arity").getMethod("invoke",
|
||||
*Array<Class<*>>(arity) { java.lang.Object::class.java })
|
||||
}
|
||||
|
||||
private fun createStaticCFunctionImpl(
|
||||
returnType: CType<Any?>,
|
||||
paramTypes: List<CType<*>>,
|
||||
function: Function<*>
|
||||
): NativePtr {
|
||||
val ffiCif = ffiCreateCif(returnType.ffiType, paramTypes.map { it.ffiType })
|
||||
|
||||
val arity = paramTypes.size
|
||||
val pt = paramTypes.toTypedArray()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val impl: FfiClosureImpl = when (arity) {
|
||||
0 -> {
|
||||
val f = function as () -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f()
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
val f = function as (Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0))
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
val f = function as (Any?, Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0), pt.read(args, 1))
|
||||
}
|
||||
}
|
||||
3 -> {
|
||||
val f = function as (Any?, Any?, Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2))
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
val f = function as (Any?, Any?, Any?, Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2), pt.read(args, 3))
|
||||
}
|
||||
}
|
||||
5 -> {
|
||||
val f = function as (Any?, Any?, Any?, Any?, Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2), pt.read(args, 3), pt.read(args, 4))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val invokeMethod = invokeMethods[arity]
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
val arguments = Array(arity) { pt.read(args, it) }
|
||||
invokeMethod.invoke(function, *arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ffiCreateClosure(ffiCif, impl)
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun Array<CType<*>>.read(args: CArrayPointer<COpaquePointerVar>, index: Int) =
|
||||
this[index].read(args[index].rawValue)
|
||||
|
||||
private inline fun ffiClosureImpl(
|
||||
returnType: CType<Any?>,
|
||||
crossinline invoke: (args: CArrayPointer<COpaquePointerVar>) -> Any?
|
||||
): FfiClosureImpl {
|
||||
return { ret, args ->
|
||||
val result = invoke(args)
|
||||
returnType.write(ret.rawValue, result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the bridge between Kotlin type `T` and the corresponding C type of a function's parameter or return value.
|
||||
* It is supposed to be constructed using the primitive types (such as [SInt32]), the [Struct] combinator
|
||||
* and the [CEnumType] wrapper.
|
||||
*
|
||||
* This description omits the details that are irrelevant for the ABI.
|
||||
*/
|
||||
private abstract class CType<T> internal constructor(val ffiType: ffi_type) {
|
||||
internal constructor(ffiTypePtr: Long) : this(interpretPointed<ffi_type>(ffiTypePtr))
|
||||
abstract fun read(location: NativePtr): T
|
||||
abstract fun write(location: NativePtr, value: T): Unit
|
||||
}
|
||||
|
||||
private object Void : CType<Any?>(ffiTypeVoid()) {
|
||||
override fun read(location: NativePtr) = throw UnsupportedOperationException()
|
||||
|
||||
override fun write(location: NativePtr, value: Any?) {
|
||||
// nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
private object SInt8 : CType<Byte>(ffiTypeSInt8()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<ByteVar>(location).value
|
||||
override fun write(location: NativePtr, value: Byte) {
|
||||
interpretPointed<ByteVar>(location).value = value
|
||||
}
|
||||
}
|
||||
|
||||
private object SInt16 : CType<Short>(ffiTypeSInt16()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<ShortVar>(location).value
|
||||
override fun write(location: NativePtr, value: Short) {
|
||||
interpretPointed<ShortVar>(location).value = value
|
||||
}
|
||||
}
|
||||
|
||||
private object SInt32 : CType<Int>(ffiTypeSInt32()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<IntVar>(location).value
|
||||
override fun write(location: NativePtr, value: Int) {
|
||||
interpretPointed<IntVar>(location).value = value
|
||||
}
|
||||
}
|
||||
|
||||
private object SInt64 : CType<Long>(ffiTypeSInt64()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<LongVar>(location).value
|
||||
override fun write(location: NativePtr, value: Long) {
|
||||
interpretPointed<LongVar>(location).value = value
|
||||
}
|
||||
}
|
||||
private object Pointer : CType<CPointer<*>?>(ffiTypePointer()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<CPointerVar<*>>(location).value
|
||||
override fun write(location: NativePtr, value: CPointer<*>?) {
|
||||
interpretPointed<CPointerVar<*>>(location).value = value
|
||||
}
|
||||
}
|
||||
|
||||
private class Struct(val size: Long, val align: Int, elementTypes: List<CType<*>>) : CType<CValue<*>>(
|
||||
ffiTypeStruct(
|
||||
elementTypes.map { it.ffiType }
|
||||
)
|
||||
) {
|
||||
override fun read(location: NativePtr) = interpretPointed<ByteVar>(location).readValue<CStructVar>(size, align)
|
||||
|
||||
override fun write(location: NativePtr, value: CValue<*>) {
|
||||
// TODO: probably CValue must be redesigned.
|
||||
val fakePlacement = object : NativePlacement {
|
||||
var used = false
|
||||
override fun alloc(size: Long, align: Int): NativePointed {
|
||||
assert(!used)
|
||||
assert (size == this@Struct.size)
|
||||
assert (align == this@Struct.align)
|
||||
used = true
|
||||
return interpretPointed<ByteVar>(location)
|
||||
}
|
||||
}
|
||||
|
||||
value.getPointer(fakePlacement)
|
||||
assert (fakePlacement.used)
|
||||
}
|
||||
}
|
||||
|
||||
private class CEnumType(private val rawValueCType: CType<Number>) : CType<CEnum>(rawValueCType.ffiType) {
|
||||
|
||||
override fun read(location: NativePtr): CEnum {
|
||||
TODO("enum-typed callback parameters")
|
||||
}
|
||||
|
||||
override fun write(location: NativePtr, value: CEnum) {
|
||||
rawValueCType.write(location, value.value)
|
||||
}
|
||||
}
|
||||
|
||||
private typealias FfiClosureImpl = (ret: COpaquePointer, args: CArrayPointer<COpaquePointerVar>)->Unit
|
||||
private typealias UserData = FfiClosureImpl
|
||||
|
||||
private fun loadCallbacksLibrary() {
|
||||
System.loadLibrary("callbacks")
|
||||
@@ -233,7 +478,7 @@ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_
|
||||
return interpretPointed(res)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Suppress("UNUSED_PARAMETER", "UNUSED")
|
||||
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
ffiFunImpl(interpretCPointer(ret)!!,
|
||||
@@ -259,8 +504,8 @@ private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
|
||||
*
|
||||
* @param ffiCif describes the type of the function to create
|
||||
*/
|
||||
private fun ffiCreateClosure(ffiCif: ffi_cif, userData: UserData): NativePtr {
|
||||
val res = ffiCreateClosure0(ffiCif.rawPtr, userData)
|
||||
private fun ffiCreateClosure(ffiCif: ffi_cif, impl: FfiClosureImpl): NativePtr {
|
||||
val res = ffiCreateClosure0(ffiCif.rawPtr, userData = impl)
|
||||
|
||||
when (res) {
|
||||
0L -> throw OutOfMemoryError()
|
||||
|
||||
@@ -52,17 +52,81 @@ fun <T : CPointed> interpretCPointer(rawValue: NativePtr) =
|
||||
CPointer<T>(rawValue)
|
||||
}
|
||||
|
||||
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
|
||||
T::class.objectInstance!!
|
||||
|
||||
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=0x%x)".format(rawValue)
|
||||
|
||||
/**
|
||||
* Returns a pointer to `T`-typed C function which calls given Kotlin *static* function.
|
||||
* @see CAdaptedFunctionType.fromStatic
|
||||
*/
|
||||
inline fun <reified F : Function<*>, reified T : CAdaptedFunctionType<F>> staticCFunction(body: F): CFunctionPointer<T> {
|
||||
val type = CAdaptedFunctionType.getInstanceOf<T>()
|
||||
return interpretPointed<CFunction<T>>(type.fromStatic(body)).ptr
|
||||
}
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class CLength(val value: Int)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class CNaturalStruct(vararg val fieldNames: String)
|
||||
|
||||
fun <R> staticCFunction(function: () -> R): CPointer<CFunction<() -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, R> staticCFunction(function: (P1) -> R): CPointer<CFunction<(P1) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, R> staticCFunction(function: (P1, P2) -> R): CPointer<CFunction<(P1, P2) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, R> staticCFunction(function: (P1, P2, P3) -> R): CPointer<CFunction<(P1, P2, P3) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, R> staticCFunction(function: (P1, P2, P3, P4) -> R): CPointer<CFunction<(P1, P2, P3, P4) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, R> staticCFunction(function: (P1, P2, P3, P4, P5) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
@@ -381,43 +381,16 @@ inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.set(index: Long
|
||||
typealias CArrayPointer<T> = CPointer<T>
|
||||
typealias CArrayPointerVar<T> = CPointerVar<T>
|
||||
|
||||
/**
|
||||
* The type of C function.
|
||||
*/
|
||||
interface CFunctionType
|
||||
|
||||
/**
|
||||
* The type of C function constructed from some Kotlin function, possibly using an adapter.
|
||||
* The (non-abstract) implementation classes are supposed to be object declarations.
|
||||
*/
|
||||
interface CAdaptedFunctionType<F : Function<*>> : CFunctionType {
|
||||
|
||||
/**
|
||||
* Returns a raw pointer to C function of this type, which calls given Kotlin *static* function.
|
||||
*
|
||||
* This inconvenient method should not be used directly; use [staticCFunction] instead.
|
||||
*
|
||||
* @param function must be *static*, i.e. an (unbound) reference to a Kotlin function or
|
||||
* a closure which doesn't capture any variable
|
||||
*/
|
||||
fun fromStatic(function: F): NativePtr
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
/**
|
||||
* The C function.
|
||||
*/
|
||||
class CFunction<T : CFunctionType>(override val rawPtr: NativePtr) : CPointed
|
||||
class CFunction<T : Function<*>>(override val rawPtr: NativePtr) : CPointed
|
||||
|
||||
/**
|
||||
* The pointer to [CFunction].
|
||||
* TODO: remove.
|
||||
* Returns a pointer to C function which calls given Kotlin *static* function.
|
||||
*
|
||||
* @param function must be *static*, i.e. an (unbound) reference to a Kotlin function or
|
||||
* a closure which doesn't capture any variable
|
||||
*/
|
||||
typealias CFunctionPointer<T> = CPointer<CFunction<T>>
|
||||
|
||||
/**
|
||||
* The variable containing a [CFunctionPointer].
|
||||
* TODO: remove.
|
||||
*/
|
||||
typealias CFunctionPointerVar<T> = CPointerVarOf<CFunctionPointer<T>>
|
||||
@Deprecated("The function type is too general. Supply argument with known arity.", level = DeprecationLevel.ERROR)
|
||||
external fun <F : Function<*>> staticCFunction(function: F): CPointer<CFunction<F>>
|
||||
@@ -45,12 +45,3 @@ private external fun disposeStablePointer(pointer: COpaquePointer)
|
||||
|
||||
@SymbolName("Kotlin_Interop_derefStablePointer")
|
||||
private external fun derefStablePointer(pointer: COpaquePointer): Any
|
||||
|
||||
/**
|
||||
* The type of C function which can be constructed from the appropriate Kotlin function without using any adapter.
|
||||
*/
|
||||
abstract class CTriviallyAdaptedFunctionType<F : Function<*>> : CAdaptedFunctionType<F> {
|
||||
@konan.internal.Intrinsic
|
||||
override final fun fromStatic(function: F): NativePtr =
|
||||
throw Error("CTriviallyAdaptedFunctionType.fromStatic is called virtually")
|
||||
}
|
||||
|
||||
@@ -34,21 +34,50 @@ fun <T : CVariable> typeOf(): CVariable.Type = throw Error("typeOf() is called w
|
||||
|
||||
@Intrinsic external fun CPointer<*>.getRawValue(): NativePtr
|
||||
|
||||
inline fun <reified T : CAdaptedFunctionType<*>> CAdaptedFunctionType.Companion.getInstanceOf(): T =
|
||||
TODO("CAdaptedFunctionType.getInstanceOf 11")
|
||||
|
||||
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=$rawValue)"
|
||||
|
||||
/**
|
||||
* Returns a pointer to `T`-typed C function which calls given Kotlin *static* function.
|
||||
* @see CAdaptedFunctionType.fromStatic
|
||||
*/
|
||||
// TODO: This function is not inline, whereas the same name function in JvmTypes.kt
|
||||
// is inline. We can't make it inline here, because native interop lowering
|
||||
// expects to find and transform it. And native interop lowering is done after
|
||||
// inline expansions.
|
||||
fun <F : Function<*>, T : CAdaptedFunctionType<F>> staticCFunction(body: F): CFunctionPointer<T> {
|
||||
val type = CAdaptedFunctionType.getInstanceOf<T>()
|
||||
return interpretPointed<CFunction<T>>(type.fromStatic(body)).ptr
|
||||
}
|
||||
@Intrinsic external fun <R> staticCFunction(function: () -> R): CPointer<CFunction<() -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, R> staticCFunction(function: (P1) -> R): CPointer<CFunction<(P1) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, R> staticCFunction(function: (P1, P2) -> R): CPointer<CFunction<(P1, P2) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, R> staticCFunction(function: (P1, P2, P3) -> R): CPointer<CFunction<(P1, P2, P3) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, R> staticCFunction(function: (P1, P2, P3, P4) -> R): CPointer<CFunction<(P1, P2, P3, P4) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, R> staticCFunction(function: (P1, P2, P3, P4, P5) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>>
|
||||
|
||||
@Intrinsic external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>>
|
||||
Reference in New Issue
Block a user