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
+1
-8
@@ -275,14 +275,7 @@ methods available:
|
||||
### Callbacks ###
|
||||
|
||||
To convert Kotlin function to pointer to C function,
|
||||
`staticCFunction(::kotlinFunction)` can be used. Currently `staticCFunction`
|
||||
heavily relies on type inference, so the expression `staticCFunction(...)`
|
||||
should be either assigned to the variable having proper type explicitly
|
||||
specified, or passed to the function, e.g.
|
||||
|
||||
```
|
||||
glutDisplayFunc(staticCFunction(::display))
|
||||
```
|
||||
`staticCFunction(::kotlinFunction)` can be used.
|
||||
|
||||
Note that some function types are not supported currently. For example,
|
||||
it is not possible to get pointer to function that receives or returns structs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -137,7 +137,7 @@ internal fun visitChildren(parent: CValue<CXCursor>, visitor: CursorVisitor) {
|
||||
clang_visitChildren(parent, staticCFunction { cursor, parent, clientData ->
|
||||
@Suppress("NAME_SHADOWING", "UNCHECKED_CAST")
|
||||
val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor
|
||||
visitor(cursor.readValue(), parent.readValue())
|
||||
visitor(cursor, parent)
|
||||
}, clientData)
|
||||
}
|
||||
|
||||
|
||||
@@ -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>>
|
||||
+53
-139
@@ -152,15 +152,6 @@ class StubGenerator(
|
||||
|
||||
val functionsToBind = nativeIndex.functions.filter { it.name !in excludedFunctions }
|
||||
|
||||
private val usedFunctionTypes = mutableMapOf<FunctionType, String>()
|
||||
|
||||
val FunctionType.kotlinName: String
|
||||
get() {
|
||||
return usedFunctionTypes.getOrPut(this, {
|
||||
"CFunctionType" + (usedFunctionTypes.size + 1)
|
||||
})
|
||||
}
|
||||
|
||||
private val macroConstantsByName = nativeIndex.macroConstants.associateBy { it.name }
|
||||
|
||||
/**
|
||||
@@ -442,7 +433,7 @@ class StubGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
is FunctionType -> byRefTypeMirror("CFunction<${type.kotlinName}>")
|
||||
is FunctionType -> byRefTypeMirror("CFunction<${getKotlinFunctionType(type)}>")
|
||||
|
||||
is Typedef -> {
|
||||
val baseType = mirror(type.def.aliased)
|
||||
@@ -485,6 +476,17 @@ class StubGenerator(
|
||||
* Constructs [OutValueBinding] for the value of given C type.
|
||||
*/
|
||||
fun getOutValueBinding(type: Type): OutValueBinding {
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
// TODO: this case should probably be handled more generally.
|
||||
val typeMirror = mirror(type)
|
||||
return OutValueBinding(
|
||||
kotlinType = "CValue<${typeMirror.pointedTypeName}>",
|
||||
kotlinConv = { name -> "$name.getPointer(memScope).rawValue" }, // TODO: eliminate this copying
|
||||
memScoped = true,
|
||||
kotlinJniBridgeType = "NativePtr"
|
||||
)
|
||||
}
|
||||
|
||||
val mirror = mirror(type)
|
||||
|
||||
return OutValueBinding(
|
||||
@@ -539,16 +541,6 @@ class StubGenerator(
|
||||
kotlinJniBridgeType = "NativePtr"
|
||||
)
|
||||
}
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
// TODO: this case should probably be handled more generally.
|
||||
val typeMirror = mirror(type)
|
||||
return OutValueBinding(
|
||||
kotlinType = "CValue<${typeMirror.pointedTypeName}>",
|
||||
kotlinConv = { name -> "$name.getPointer(memScope).rawValue" }, // TODO: eliminate this copying
|
||||
memScoped = true,
|
||||
kotlinJniBridgeType = "NativePtr"
|
||||
)
|
||||
}
|
||||
|
||||
return getOutValueBinding(type)
|
||||
}
|
||||
@@ -569,6 +561,15 @@ class StubGenerator(
|
||||
* Constructs [InValueBinding] for the value of given C type.
|
||||
*/
|
||||
fun getInValueBinding(type: Type): InValueBinding {
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
val typeMirror = mirror(type)
|
||||
return InValueBinding(
|
||||
kotlinJniBridgeType = "NativePtr",
|
||||
conv = { name -> "interpretPointed<${typeMirror.pointedTypeName}>($name).readValue()" },
|
||||
kotlinType = "CValue<${typeMirror.pointedTypeName}>"
|
||||
)
|
||||
}
|
||||
|
||||
val mirror = mirror(type)
|
||||
|
||||
return InValueBinding(
|
||||
@@ -581,15 +582,6 @@ class StubGenerator(
|
||||
fun getCFunctionRetValBinding(func: FunctionDecl): InValueBinding {
|
||||
when {
|
||||
func.returnsVoid() -> return InValueBinding("Unit")
|
||||
func.returnsRecord() -> {
|
||||
// TODO: this case should probably be handled more generally.
|
||||
val typeMirror = mirror(func.returnType)
|
||||
return InValueBinding(
|
||||
kotlinJniBridgeType = "NativePtr",
|
||||
conv = { name -> "interpretPointed<${typeMirror.pointedTypeName}>($name).readValue()" },
|
||||
kotlinType = "CValue<${typeMirror.pointedTypeName}>"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return getInValueBinding(func.returnType)
|
||||
@@ -606,6 +598,23 @@ class StubGenerator(
|
||||
getCallbackRetValBinding(type.returnType).kotlinType
|
||||
}
|
||||
|
||||
private fun getArrayLength(type: ArrayType): Long {
|
||||
val unwrappedElementType = type.elemType.unwrapTypedefs()
|
||||
val elementLength = if (unwrappedElementType is ArrayType) {
|
||||
getArrayLength(unwrappedElementType)
|
||||
} else {
|
||||
1L
|
||||
}
|
||||
|
||||
val elementCount = when (type) {
|
||||
is ConstArrayType -> type.length
|
||||
is IncompleteArrayType -> 0L
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
return elementLength * elementCount
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces to [out] the definition of Kotlin class representing the reference to given struct.
|
||||
*/
|
||||
@@ -616,6 +625,12 @@ class StubGenerator(
|
||||
return
|
||||
}
|
||||
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
if (def.hasNaturalLayout) {
|
||||
out("@CNaturalStruct(${def.fields.joinToString { it.name.quoteAsKotlinLiteral() }})")
|
||||
}
|
||||
}
|
||||
|
||||
block("class ${decl.kotlinName.asSimpleName()}(override val rawPtr: NativePtr) : CStructVar()") {
|
||||
out("")
|
||||
out("companion object : Type(${def.size}, ${def.align})") // FIXME: align
|
||||
@@ -628,9 +643,17 @@ class StubGenerator(
|
||||
assert(field.offset % 8 == 0L)
|
||||
val offset = field.offset / 8
|
||||
val fieldRefType = mirror(field.type)
|
||||
if (field.type.unwrapTypedefs() is ArrayType) {
|
||||
val unwrappedFieldType = field.type.unwrapTypedefs()
|
||||
if (unwrappedFieldType is ArrayType) {
|
||||
val type = (fieldRefType as TypeMirror.ByValue).valueTypeName
|
||||
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
val length = getArrayLength(unwrappedFieldType)
|
||||
|
||||
// TODO: @CLength should probably be used on types instead of properties.
|
||||
out("@CLength($length)")
|
||||
}
|
||||
|
||||
out("val ${field.name.asSimpleName()}: $type")
|
||||
out(" get() = arrayMemberAt($offset)")
|
||||
} else {
|
||||
@@ -990,51 +1013,6 @@ class StubGenerator(
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun getFfiStructType(elementTypes: List<Type>) =
|
||||
"Struct(" +
|
||||
elementTypes.map { getFfiType(it) }.joinToString(", ") +
|
||||
")"
|
||||
|
||||
private fun getFfiType(type: Type): String {
|
||||
return when(type) {
|
||||
is VoidType -> "Void"
|
||||
is CharType -> "SInt8" // TODO: libffi has separate representation for char type.
|
||||
is IntegerType -> when (type.size) {
|
||||
1 -> if (type.isSigned) "SInt8" else "UInt8"
|
||||
2 -> if (type.isSigned) "SInt16" else "UInt16"
|
||||
4 -> if (type.isSigned) "SInt32" else "UInt32"
|
||||
8 -> if (type.isSigned) "SInt64" else "UInt64"
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
is PointerType -> "Pointer"
|
||||
is ConstArrayType -> getFfiStructType(
|
||||
Array(type.length.toInt(), { type.elemType }).toList()
|
||||
)
|
||||
is EnumType -> getFfiType(type.def.baseType)
|
||||
is RecordType -> {
|
||||
val def = type.decl.def!!
|
||||
if (!def.hasNaturalLayout) {
|
||||
throw NotImplementedError(type.kotlinName)
|
||||
}
|
||||
getFfiStructType(def.fields.map { it.type })
|
||||
}
|
||||
is Typedef -> getFfiType(type.def.aliased)
|
||||
else -> throw NotImplementedError(type.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getArgFfiType(type: Type) = when (type.unwrapTypedefs()) {
|
||||
is ArrayType -> "Pointer"
|
||||
else -> getFfiType(type)
|
||||
}
|
||||
|
||||
private fun getRetValFfiType(type: Type) = getArgFfiType(type)
|
||||
|
||||
private fun generateFunctionType(type: FunctionType, name: String) = when (platform) {
|
||||
KotlinPlatform.JVM -> generateJvmFunctionType(type, name)
|
||||
KotlinPlatform.NATIVE -> generateNativeFunctionType(type, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` iff the function binding for Kotlin Native
|
||||
* requires non-trivial Kotlin adapter to convert arguments.
|
||||
@@ -1064,64 +1042,6 @@ class StubGenerator(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun FunctionType.requiresAdapterOnNative(): Boolean {
|
||||
assert (platform == KotlinPlatform.NATIVE)
|
||||
return (parameterTypes + returnType).any {
|
||||
val unwrappedType = it.unwrapTypedefs()
|
||||
unwrappedType is RecordType || (unwrappedType is EnumType && unwrappedType.def.isStrictEnum)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateNativeFunctionType(type: FunctionType, name: String) {
|
||||
if (type.requiresAdapterOnNative()) {
|
||||
out("object $name : CFunctionType {}")
|
||||
} else {
|
||||
val kotlinFunctionType = getKotlinFunctionType(type)
|
||||
out("object $name : CTriviallyAdaptedFunctionType<$kotlinFunctionType>()")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateJvmFunctionType(type: FunctionType, name: String) {
|
||||
val kotlinFunctionType = getKotlinFunctionType(type)
|
||||
|
||||
val constructorArgs = try {
|
||||
listOf(getRetValFfiType(type.returnType)) +
|
||||
type.parameterTypes.map { getArgFfiType(it) }
|
||||
|
||||
} catch (e: Throwable) {
|
||||
log("Warning: cannot generate definition for function type $name")
|
||||
out("object $name : CFunctionType {}")
|
||||
return
|
||||
}
|
||||
|
||||
val constructorArgsStr = constructorArgs.joinToString(", ")
|
||||
|
||||
block("object $name : CAdaptedFunctionTypeImpl<$kotlinFunctionType>($constructorArgsStr)") {
|
||||
block("override fun invoke(function: $kotlinFunctionType, args: CArrayPointer<COpaquePointerVar>, ret: COpaquePointer)") {
|
||||
val args = type.parameterTypes.mapIndexed { i, paramType ->
|
||||
val pointedTypeName = mirror(paramType).pointedTypeName
|
||||
val ref = "args[$i]!!.reinterpret<$pointedTypeName>().pointed"
|
||||
when (paramType.unwrapTypedefs()) {
|
||||
is RecordType -> ref
|
||||
else -> "$ref.value"
|
||||
}
|
||||
}.joinToString(", ")
|
||||
|
||||
out("val res = function($args)")
|
||||
|
||||
when (type.returnType.unwrapTypedefs()) {
|
||||
is RecordType -> throw NotImplementedError()
|
||||
is VoidType -> {} // nothing to do
|
||||
else -> {
|
||||
val pointedTypeName = mirror(type.returnType).pointedTypeName
|
||||
out("ret.reinterpret<$pointedTypeName>().pointed.value = res")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun integerLiteral(type: Type, value: Long): String? {
|
||||
if (value == Long.MIN_VALUE) {
|
||||
return "${value + 1} - 1" // Workaround for "The value is out of range" compile error.
|
||||
@@ -1287,12 +1207,6 @@ class StubGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
usedFunctionTypes.entries.forEach {
|
||||
stubs.add(
|
||||
generateKotlinFragmentBy { generateFunctionType(it.key, it.value) }
|
||||
)
|
||||
}
|
||||
|
||||
return stubs
|
||||
}
|
||||
|
||||
|
||||
+1
-14
@@ -144,20 +144,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
|
||||
val bitsToDouble = packageScope.getContributedFunctions("bitsToDouble").single()
|
||||
|
||||
val staticCFunction = packageScope.getContributedFunctions("staticCFunction").single()
|
||||
|
||||
private val triviallyAdaptedFunctionTypeClass =
|
||||
packageScope.getContributedClassifier("CTriviallyAdaptedFunctionType") as ClassDescriptor
|
||||
|
||||
private val trivallyAdaptedFunctionTypeType =
|
||||
triviallyAdaptedFunctionTypeClass.defaultType.replace(
|
||||
newArguments = listOf(
|
||||
StarProjectionImpl(triviallyAdaptedFunctionTypeClass.declaredTypeParameters.single())
|
||||
)
|
||||
)
|
||||
|
||||
fun isTriviallyAdaptedFunctionType(type: KotlinType): Boolean =
|
||||
type.isSubtypeOf(trivallyAdaptedFunctionTypeType)
|
||||
val staticCFunction = packageScope.getContributedFunctions("staticCFunction").toSet()
|
||||
|
||||
val signExtend = packageScope.getContributedFunctions("signExtend").single()
|
||||
|
||||
|
||||
+50
-11
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
||||
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
||||
import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
@@ -275,27 +276,42 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
|
||||
builder.readValue(receiver, typeArgument) ?: expression
|
||||
}
|
||||
|
||||
interop.staticCFunction -> {
|
||||
in interop.staticCFunction -> {
|
||||
val argument = expression.getValueArgument(0)!!
|
||||
if (argument !is IrCallableReference || argument.getArguments().isNotEmpty()) {
|
||||
context.reportCompilationError(
|
||||
"${interop.staticCFunction.fqNameSafe} must take an unbound, non-capturing function",
|
||||
"${descriptor.fqNameSafe} must take an unbound, non-capturing function",
|
||||
irFile, expression
|
||||
)
|
||||
// TODO: should probably be reported during analysis.
|
||||
}
|
||||
|
||||
val cFunctionType = expression.getTypeArgument(descriptor.typeParameters[1])!!
|
||||
val target = argument.descriptor.original
|
||||
val signatureTypes = target.allParameters.map { it.type } + target.returnType!!
|
||||
|
||||
if (interop.isTriviallyAdaptedFunctionType(cFunctionType)) {
|
||||
IrCallableReferenceImpl(
|
||||
builder.startOffset, builder.endOffset,
|
||||
expression.type,
|
||||
argument.descriptor,
|
||||
typeArguments = null)
|
||||
} else {
|
||||
TODO("$cFunctionType requiring non-trivial C adapter")
|
||||
signatureTypes.forEachIndexed { index, type ->
|
||||
type.ensureSupportedInCallbacks(
|
||||
isReturnType = (index == signatureTypes.lastIndex),
|
||||
reportError = { context.reportCompilationError(it, irFile, expression) }
|
||||
)
|
||||
}
|
||||
|
||||
descriptor.typeParameters.forEachIndexed { index, typeParameterDescriptor ->
|
||||
val typeArgument = expression.getTypeArgument(typeParameterDescriptor)!!
|
||||
val signatureType = signatureTypes[index]
|
||||
if (typeArgument != signatureType) {
|
||||
context.reportCompilationError(
|
||||
"C function signature element mismatch: expected '$signatureType', got '$typeArgument'",
|
||||
irFile, expression
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
IrCallableReferenceImpl(
|
||||
builder.startOffset, builder.endOffset,
|
||||
expression.type,
|
||||
target,
|
||||
typeArguments = null)
|
||||
}
|
||||
|
||||
interop.signExtend, interop.narrow -> {
|
||||
@@ -346,6 +362,29 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.ensureSupportedInCallbacks(isReturnType: Boolean, reportError: (String) -> Nothing) {
|
||||
if (isReturnType && KotlinBuiltIns.isUnit(this)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(this)) {
|
||||
if (!this.isMarkedNullable) {
|
||||
return
|
||||
}
|
||||
reportError("Type $this must not be nullable when used in callback signature")
|
||||
}
|
||||
|
||||
if (TypeUtils.getClassDescriptor(this) == interop.cPointer) {
|
||||
if (this.isMarkedNullable) {
|
||||
return
|
||||
}
|
||||
|
||||
reportError("Type $this must be nullable when used in callback signature")
|
||||
}
|
||||
|
||||
reportError("Type $this is not supported in callback signature")
|
||||
}
|
||||
|
||||
private fun IrCall.getSingleTypeArgument(): KotlinType {
|
||||
val typeParameter = descriptor.original.typeParameters.single()
|
||||
return getTypeArgument(typeParameter)!!
|
||||
|
||||
Reference in New Issue
Block a user