Skip to content

Commit

Permalink
Merge pull request #6 from korlibs/soywiz/support.string.as.parameter
Browse files Browse the repository at this point in the history
Support String as parameter for FFI functions
  • Loading branch information
soywiz authored Jul 30, 2024
2 parents b3e5080 + 844e062 commit 075b557
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ $catalog.json
/stagedRepositoryId
/*/build
.DS_Store
TEST.BIN
43 changes: 36 additions & 7 deletions korlibs-ffi-ksp/src@jvm/korlibs/ffi/ksp/FFIBuilderProcessor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ private class FFIBuilderProcessor(val environment: SymbolProcessorEnvironment) :
it.appendLine("}")
}
isJs -> {
it.appendLine("fun DenoPointer_to_FFIPointer(v: dynamic): FFIPointer = FFIPointer(js(\"Deno.UnsafePointer.value(v)\").toString().toLong())")
it.appendLine("fun FFIPointer_to_DenoPointer(v: FFIPointer): dynamic { val vv = v.address.toString(); return js(\"Deno.UnsafePointer.create(BigInt(vv))\") }")
it.appendLine("private fun String_to_DenoPointer(str: String): dynamic = js(\"(Deno.UnsafePointer.of(new TextEncoder().encode(str)))\")")
it.appendLine("private fun DenoPointer_to_FFIPointer(v: dynamic): FFIPointer = FFIPointer(js(\"Deno.UnsafePointer.value(v)\").toString().toLong())")
it.appendLine("private fun FFIPointer_to_DenoPointer(v: FFIPointer): dynamic { val vv = v.address.toString(); return js(\"Deno.UnsafePointer.create(BigInt(vv))\") }")

it.appendLine("private fun __load_$classNameImpl() = js(\"\"\"")
it.appendLine(" (typeof Deno === 'undefined') ? {} : Deno.dlopen(Deno.build.os === 'windows' ? '$libraryNameWin' : Deno.build.os === 'darwin' ? '$libraryNameMac' : '$libraryNameLinux', {")
Expand Down Expand Up @@ -178,8 +179,19 @@ private class FFIBuilderProcessor(val environment: SymbolProcessorEnvironment) :

val defaultCasts = object : PlatformCasts {}
val denoCasts = object : PlatformCasts {
override fun cast(str: String, type: KSTypeReference?): String = type.asString().let { if (it == "FFIPointer") "DenoPointer_to_FFIPointer($str)" else str }
override fun revCast(str: String, type: KSTypeReference?): String = type.asString().let { if (it == "FFIPointer") "FFIPointer_to_DenoPointer($str)" else str }
override fun cast(str: String, type: KSTypeReference?): String = type.asString().let {
when (it) {
"FFIPointer" -> "DenoPointer_to_FFIPointer($str)"
else -> str
}
}
override fun revCast(str: String, type: KSTypeReference?): String = type.asString().let {
when (it) {
"FFIPointer" -> "FFIPointer_to_DenoPointer($str)"
"String" -> "String_to_DenoPointer($str)"
else -> str
}
}
}
val jnaCasts = object : PlatformCasts {
override fun typeProcessor(type: KSTypeReference?): String = type.asString().let {
Expand All @@ -193,9 +205,26 @@ private class FFIBuilderProcessor(val environment: SymbolProcessorEnvironment) :
override fun revCast(str: String, type: KSTypeReference?): String = type.asString().let { if (it == "FFIPointer") "$str.toPointer()" else str }
}
val knativeCasts = object : PlatformCasts {
override fun typeProcessor(type: KSTypeReference?): String = type.asString().let { if (it == "FFIPointer") "COpaquePointer?" else it }
override fun cast(str: String, type: KSTypeReference?): String = type.asString().let { if (it == "FFIPointer") "$str.toFFIPointer()" else str }
override fun revCast(str: String, type: KSTypeReference?): String = type.asString().let { if (it == "FFIPointer") "$str.toPointer()" else str }
override fun typeProcessor(type: KSTypeReference?): String = type.asString().let {
when (it) {
"FFIPointer" -> "COpaquePointer?"
"String" -> "CValues<ByteVar>"
else -> it
}
}
override fun cast(str: String, type: KSTypeReference?): String = type.asString().let {
when (it) {
"FFIPointer" -> "$str.toFFIPointer()"
else -> str
}
}
override fun revCast(str: String, type: KSTypeReference?): String = type.asString().let {
when (it) {
"FFIPointer" -> "$str.toPointer()"
"String" -> "$str.cstr"
else -> str
}
}
}
}

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 @@ -12,6 +12,9 @@ annotation class FFINativeInt
annotation class FFIWideString

inline class FFIPointer(val address: Long) {
companion object {
val NULL = FFIPointer(0L)
}
@OptIn(ExperimentalStdlibApi::class)
override fun toString(): String = "FFIPointer(address=0x${address.toHexString()})"
}
Expand Down
3 changes: 3 additions & 0 deletions korlibs-ffi/src/korlibs/ffi/api/TestMathFFI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ internal interface TestMathFFI : AutoCloseable {
//fun qsort(base: FFIPointer, number: Int, width: Int, compare: FFIFunctionRef<(FFIPointer, FFIPointer) -> Int>)
//fun puts(str: String): Int
//fun fputs(str: String, file: FFIPointer): Int
fun fopen(file: String, mode: String): FFIPointer
fun fclose(ptr: FFIPointer)
fun remove(file: String)

companion object : TestMathFFI by TestMathFFI() {
operator fun invoke(): TestMathFFI = TODO()
Expand Down
13 changes: 12 additions & 1 deletion korlibs-ffi/test/korlibs/ffi/api/FFIGenerationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@ import kotlin.test.*
class FFIGenerationTest {
@Test
fun test() {
if (!isSupportedFFI) return
if (!isSupportedFFI) {
println("Skipping FFIGenerationTest.test since FFI is not supported in this target")
return
}

TestMathFFI().use { lib ->
assertEquals(1f, lib.cosf(0f))
//lib.puts("hello world\r\n")
val ptr = lib.malloc(40)
//lib.qsort(ptr, 10, 4, FFIFunctionRef { l, r -> 0 })

lib.fopen("TEST.BIN", "wb").also { file ->
if (file != FFIPointer.NULL) {
lib.fclose(file)
}
}
lib.remove("TEST.BIN")

println(ptr)
lib.free(ptr)
println("AFTER FREE: $ptr")
Expand Down

0 comments on commit 075b557

Please sign in to comment.