Skip to content

Commit

Permalink
Fixes JS callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
soywiz committed Jul 30, 2024
1 parent c1e4082 commit d5f3430
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 10 deletions.
63 changes: 54 additions & 9 deletions korlibs-ffi-ksp/src@jvm/korlibs/ffi/ksp/FFIBuilderProcessor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ private class FFIBuilderProcessor(val environment: SymbolProcessorEnvironment) :
it.appendLine(" (typeof Deno === 'undefined') ? {} : Deno.dlopen(Deno.build.os === 'windows' ? '$libraryNameWin' : Deno.build.os === 'darwin' ? '$libraryNameMac' : '$libraryNameLinux', {")
for (func in sym.getDeclaredFunctions()) {
it.appendLine(" \"${func.sname}\": { parameters: [${func.parameters.joinToString(", ") {
"\"" + jsType(it.type) + "\""
}}], result: \"${jsType(func.returnType)}\" },")
jsTypeStr(it.type)
}}], result: ${jsTypeStr(func.returnType)} },")
}
it.appendLine(" })")
it.appendLine("\"\"\")")
Expand All @@ -193,7 +193,18 @@ private class FFIBuilderProcessor(val environment: SymbolProcessorEnvironment) :
}

// `void`, `bool`, `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `usize`, `isize`, `f32`, `f64`, `pointer`, `buffer`, `function`, `struct`
private fun jsType(type: KSTypeReference?): String = when (type.asString()) {
private fun jsTypeStr(type: KSTypeReference?): String {
val res = __jsType(type)
return when (res) {
is String -> "'$res'"
else -> TODO()
}
}

@Deprecated("Use jsTypeStr")
private fun jsType(type: KSTypeReference?): String = __jsType(type).toString()

private fun __jsType(type: KSTypeReference?): Any = when (type.asString()) {
"Unit" -> "void"
"Boolean" -> "bool"
"UByte" -> "u8"
Expand Down Expand Up @@ -227,7 +238,43 @@ private class FFIBuilderProcessor(val environment: SymbolProcessorEnvironment) :
when (it) {
"FFIPointer" -> "FFIPointer_to_DenoPointer($str)"
"String" -> "String_to_DenoPointer($str)"
else -> str
else -> when {
it.startsWith("FFIFunctionRef<") -> {
// fun interface QsortCompareCallback : StdCallLibrary { fun callback(l: FFIPointer, r: FFIPointer): Int }
//"{ l, r -> $str.func(l, r) }"

/*
val name = ffi.getName(callback)
val funcType = callback.funcType
val params = callback.paramFuncParams.asString(casts, callback)
val retType = callback.paramFuncRet.type.asString(casts, callback)
val NCallbacks = 4
for (n in 0 until NCallbacks) {
it.appendLine("private fun ${name}_$n($params): $retType = fun_$name[$n]!!.func(${callback.paramFuncParams.asCallString(casts, callback)})")
}
val funcs = (0 until NCallbacks).joinToString(", ") { "staticCFunction(::${name}_$it)" }
it.appendLine("private val fun_$name = kotlin.arrayOfNulls<${callback.param!!.type.asString()}>($NCallbacks)")
it.appendLine("private val ref_$name = listOf($funcs)")
it.appendLine("private fun alloc_$name(cfunc: ${callback.param.type.asString()}) = ref_$name[cfunc.allocIn(fun_$name)]")
*/

val dargs = context.paramFuncParams.withIndex().joinToString(", ") { "__p${it.index}: dynamic" }
val dcall = context.paramFuncParams.asCallString(this, context)
val jsParamTypes = context.paramFuncParams.joinToString(", ") { jsTypeStr(it.type) }
val jsRetType = jsTypeStr(context.paramFuncRet.type)

"""
run {
val func = fun($dargs): dynamic { return ${context.param!!.name!!.asString()}.func(${dcall}) }
val callback = js("(new Deno.UnsafeCallback({ parameters: [$jsParamTypes], result: $jsRetType }, func))")
compare.closer = { callback.close() }
callback.pointer
}
""".trimIndent()
}
else -> str
}
}
}
}
Expand Down Expand Up @@ -268,9 +315,7 @@ private class FFIBuilderProcessor(val environment: SymbolProcessorEnvironment) :
"FFIFunctionRef" -> {
TODO()
}
else -> {
str
}
else -> str
}
}
override fun revCast(str: String, type: KSTypeReference?, context: FuncContext): String = type.asString().let {
Expand Down Expand Up @@ -350,10 +395,10 @@ private fun KSTypeReference?.asString(casts: PlatformCasts = PlatformCasts, cont
@JvmName("List_asStringKSTypeReference")
private fun List<KSTypeReference?>.asString(casts: PlatformCasts = PlatformCasts, context: FuncContext): String = joinToString(", ") { casts.typeProcessor(it, context) }
@JvmName("List_asStringKSTypeArgument")
private fun List<KSTypeArgument>.asString(casts: PlatformCasts = PlatformCasts, context: FuncContext): String = withIndex().joinToString(", ") { "p${it.index}: ${casts.typeProcessor(it.value.type, context)}" }
private fun List<KSTypeArgument>.asString(casts: PlatformCasts = PlatformCasts, context: FuncContext): String = withIndex().joinToString(", ") { "__p${it.index}: ${casts.typeProcessor(it.value.type, context)}" }

@JvmName("List_asCallStringKSTypeArgument")
private fun List<KSTypeArgument>.asCallString(casts: PlatformCasts = PlatformCasts, context: FuncContext): String = withIndex().joinToString(", ") { casts.cast("p${it.index}", it.value.type, context) }
private fun List<KSTypeArgument>.asCallString(casts: PlatformCasts = PlatformCasts, context: FuncContext): String = withIndex().joinToString(", ") { casts.cast("__p${it.index}", it.value.type, context) }

private fun List<KSValueParameter>.asString(casts: PlatformCasts = PlatformCasts, context: FuncContext): String = joinToString(", ") { "${it.name?.asString()}: ${casts.typeProcessor(it.type, context.copy(param = it))}" }
private fun List<KSValueParameter>.asTypeString(casts: PlatformCasts = PlatformCasts, context: FuncContext): String = joinToString(", ") { casts.typeProcessor(it.type, context.copy(param = it)) }
Expand Down
3 changes: 3 additions & 0 deletions korlibs-ffi/src/korlibs/ffi/api/FFI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ inline class FFIPointer(val address: Long) {
class FFIFunctionRef<T : Function<*>>(val func: T) : AutoCloseable {
var slot: Int = -1
var slots: Array<FFIFunctionRef<T>?>? = null
var closer: (() -> Unit)? = null

fun allocIn(slots: Array<FFIFunctionRef<T>?>): Int {
if (slot >= 0) return slot
Expand All @@ -33,6 +34,8 @@ class FFIFunctionRef<T : Function<*>>(val func: T) : AutoCloseable {
}

override fun close() {
closer?.invoke()
closer = null
if (slot >= 0) this.slots?.set(slot, null)
slot = -1
this.slots = null
Expand Down
4 changes: 3 additions & 1 deletion korlibs-ffi/test/korlibs/ffi/api/FFIGenerationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ class FFIGenerationTest {
listOf(0, 0, 777777777, 1928074899, -100679232, 1083181060, -1487162319, -1317266793, -167789696, -2027935736),
(0 until 10).map { ptr.getI32(it * 4) }
)
lib.qsort(ptr, 10, 4, FFIFunctionRef { l, r -> l.getI32().compareTo(r.getI32()) })
FFIFunctionRef { l: FFIPointer, r: FFIPointer -> l.getI32().compareTo(r.getI32()) }.use {
lib.qsort(ptr, 10, 4, it)
}
assertEquals(
listOf(-2027935736, -1487162319, -1317266793, -167789696, -100679232, 0, 0, 777777777, 1083181060, 1928074899),
(0 until 10).map { ptr.getI32(it * 4) }
Expand Down

0 comments on commit d5f3430

Please sign in to comment.