ffi: explicit parameter type conversion

This commit is contained in:
Alex Zenla 2023-09-12 01:16:11 -04:00
parent 4053c5825a
commit 260c9f437d
Signed by: alex
GPG Key ID: C0780728420EBFE5
2 changed files with 58 additions and 4 deletions

View File

@ -3,7 +3,8 @@ package gay.pizza.pork.ffi
class FfiFunctionDefinition(
val library: String,
val function: String,
val returnType: String
val returnType: String,
val parameters: List<String>
) {
companion object {
fun parse(def: String): FfiFunctionDefinition {
@ -15,7 +16,13 @@ class FfiFunctionDefinition(
"but '${def}' was specified")
}
val (library, function, returnType) = parts
return FfiFunctionDefinition(library, function, returnType)
val parametersString = if (parts.size == 4) parts[3] else ""
return FfiFunctionDefinition(
library,
function,
returnType,
parametersString.split(",")
)
}
}
}

View File

@ -1,6 +1,7 @@
package gay.pizza.pork.ffi
import com.sun.jna.Function
import com.sun.jna.Pointer
import gay.pizza.pork.ast.ArgumentSpec
import gay.pizza.pork.evaluator.CallableFunction
import gay.pizza.pork.evaluator.NativeProvider
@ -12,20 +13,22 @@ class JnaNativeProvider : NativeProvider {
return CallableFunction { functionArgs ->
val ffiArgs = mutableListOf<Any?>()
for ((index, spec) in arguments.withIndex()) {
val ffiType = functionDefinition.parameters[index]
if (spec.multiple) {
val variableArguments = functionArgs.values
.subList(index, functionArgs.values.size)
ffiArgs.addAll(variableArguments)
break
} else {
ffiArgs.add(functionArgs.values[index])
val converted = convert(ffiType, functionArgs.values[index])
ffiArgs.add(converted)
}
}
invoke(function, ffiArgs.toTypedArray(), functionDefinition.returnType)
}
}
private fun invoke(function: Function, values: Array<Any?>, type: String): Any = when (type) {
private fun invoke(function: Function, values: Array<Any?>, type: String): Any = when (rewriteType(type)) {
"void*" -> function.invokePointer(values)
"int" -> function.invokeInt(values)
"long" -> function.invokeLong(values)
@ -35,4 +38,48 @@ class JnaNativeProvider : NativeProvider {
"char*" -> function.invokeString(values, false)
else -> throw RuntimeException("Unsupported ffi return type: $type")
}
private fun rewriteType(type: String): String = when (type) {
"size_t" -> "long"
else -> type
}
private fun convert(type: String, value: Any?): Any? = when (rewriteType(type)) {
"short" -> numberConvert(type, value) { toShort() }
"unsigned short" -> numberConvert(type, value) { toShort().toUShort() }
"int" -> numberConvert(type, value) { toInt() }
"unsigned int" -> numberConvert(type, value) { toInt().toUInt() }
"long" -> numberConvert(type, value) { toLong() }
"unsigned long" -> numberConvert(type, value) { toLong().toULong() }
"double" -> numberConvert(type, value) { toDouble() }
"float" -> numberConvert(type, value) { toFloat() }
"char*" -> notNullConvert(type, value) { toString() }
"void*" -> nullableConvert(type, value) { this as Pointer }
else -> throw RuntimeException("Unsupported ffi type: $type")
}
private fun <T> notNullConvert(type: String, value: Any?, into: Any.() -> T): T {
if (value == null) {
throw RuntimeException("Null values cannot be used for converting to type $type")
}
return into(value)
}
private fun <T> nullableConvert(type: String, value: Any?, into: Any.() -> T): T? {
if (value == null) {
return null
}
return into(value)
}
private fun <T> numberConvert(type: String, value: Any?, into: Number.() -> T): T {
if (value == null) {
throw RuntimeException("Null values cannot be used for converting to numeric type $type")
}
if (value !is Number) {
throw RuntimeException("Cannot convert value '$value' into type $type")
}
return into(value)
}
}