Skip to content

Commit

Permalink
ffi: support for java native functions
Browse files Browse the repository at this point in the history
  • Loading branch information
azenla committed Sep 7, 2023
1 parent 81296ee commit 953679b
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 4 deletions.
10 changes: 10 additions & 0 deletions examples/java.pork
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
func java_system_err()
native java "java.lang.System:static-getter:err:java.io.PrintStream"

func print_stream_println(stream, line)
native java "java.io.PrintStream:virtual:println:void:java.lang.String"

export func main() {
let error = java_system_err()
print_stream_println(error, "Hello World")
}
20 changes: 20 additions & 0 deletions ffi/src/main/kotlin/gay/pizza/pork/ffi/FfiFunctionDefinition.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package gay.pizza.pork.ffi

class FfiFunctionDefinition(
val library: String,
val function: String,
val returnType: String
) {
companion object {
fun parse(def: String): FfiFunctionDefinition {
val parts = def.split(":", limit = 3)
if (parts.size != 3 || parts.any { it.trim().isEmpty() }) {
throw RuntimeException(
"FFI function definition is invalid, " +
"excepted format is 'library:function:return-type' but '${def}' was specified")
}
val (library, function, returnType) = parts
return FfiFunctionDefinition(library, function, returnType)
}
}
}
23 changes: 23 additions & 0 deletions ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaFunctionDefinition.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package gay.pizza.pork.ffi

class JavaFunctionDefinition(
val type: String,
val kind: String,
val symbol: String,
val returnType: String,
val parameters: List<String>
) {
companion object {
fun parse(def: String): JavaFunctionDefinition {
val parts = def.split(":", limit = 5)
if (!(parts.size == 4 || parts.size == 5) || parts.any { it.trim().isEmpty() }) {
throw RuntimeException(
"Java function definition is invalid, " +
"excepted format is 'type:kind:symbol:return-type:(optional)parameters' but '${def}' was specified")
}
val (type, kind, symbol, returnType) = parts
val parameters = if (parts.size > 4) parts[4].split(",") else emptyList()
return JavaFunctionDefinition(type, kind, symbol, returnType, parameters)
}
}
}
42 changes: 42 additions & 0 deletions ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaNativeProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package gay.pizza.pork.ffi

import gay.pizza.pork.evaluator.CallableFunction
import gay.pizza.pork.evaluator.NativeFunctionProvider
import gay.pizza.pork.evaluator.None
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType

class JavaNativeProvider : NativeFunctionProvider {
private val lookup = MethodHandles.lookup()

override fun provideNativeFunction(definition: String): CallableFunction {
val functionDefinition = JavaFunctionDefinition.parse(definition)
val javaClass = lookupClass(functionDefinition.type)
val returnTypeClass = lookupClass(functionDefinition.returnType)
val parameterClasses = functionDefinition.parameters.map { lookup.findClass(it) }
val handle = mapKindToHandle(
functionDefinition.kind,
functionDefinition.symbol,
javaClass,
returnTypeClass,
parameterClasses
)
return CallableFunction { arguments -> handle.invokeWithArguments(arguments.values) ?: None }
}

private fun lookupClass(name: String): Class<*> = when (name) {
"void" -> Void.TYPE
else -> lookup.findClass(name)
}

private fun mapKindToHandle(kind: String, symbol: String, javaClass: Class<*>, returnType: Class<*>, parameterTypes: List<Class<*>>) = when (kind) {
"getter" -> lookup.findGetter(javaClass, symbol, returnType)
"setter" -> lookup.findSetter(javaClass, symbol, returnType)
"constructor" -> lookup.findConstructor(javaClass, MethodType.methodType(returnType, parameterTypes))
"static" -> lookup.findStatic(javaClass, symbol, MethodType.methodType(returnType, parameterTypes))
"virtual" -> lookup.findVirtual(javaClass, symbol, MethodType.methodType(returnType, parameterTypes))
"static-getter" -> lookup.findStaticGetter(javaClass, symbol, returnType)
"static-setter" -> lookup.findStaticSetter(javaClass, symbol, returnType)
else -> throw RuntimeException("Unknown Handle Kind: $kind")
}
}
7 changes: 3 additions & 4 deletions ffi/src/main/kotlin/gay/pizza/pork/ffi/JnaNativeProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import gay.pizza.pork.evaluator.NativeFunctionProvider

class JnaNativeProvider : NativeFunctionProvider {
override fun provideNativeFunction(definition: String): CallableFunction {
val (libraryName, functionSymbol, returnType, _) =
definition.split(":", limit = 3)
val function = Function.getFunction(libraryName, functionSymbol)
val functionDefinition = FfiFunctionDefinition.parse(definition)
val function = Function.getFunction(functionDefinition.library, functionDefinition.function)
return CallableFunction {
return@CallableFunction invoke(function, it.values.toTypedArray(), returnType)
return@CallableFunction invoke(function, it.values.toTypedArray(), functionDefinition.returnType)
}
}

Expand Down
2 changes: 2 additions & 0 deletions tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import gay.pizza.pork.evaluator.Arguments
import gay.pizza.pork.evaluator.CallableFunction
import gay.pizza.pork.evaluator.None
import gay.pizza.pork.evaluator.Scope
import gay.pizza.pork.ffi.JavaNativeProvider
import gay.pizza.pork.ffi.JnaNativeProvider

class RunCommand : CliktCommand(help = "Run Program", name = "run") {
Expand All @@ -32,6 +33,7 @@ class RunCommand : CliktCommand(help = "Run Program", name = "run") {

val main = tool.loadMainFunction(scope, setupEvaluator = {
addNativeFunctionProvider("ffi", JnaNativeProvider())
addNativeFunctionProvider("java", JavaNativeProvider())
})

maybeLoopAndMeasure(loop, measure) {
Expand Down

0 comments on commit 953679b

Please sign in to comment.