diff --git a/examples/java.pork b/examples/java.pork new file mode 100644 index 0000000..3aab150 --- /dev/null +++ b/examples/java.pork @@ -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") +} diff --git a/ffi/src/main/kotlin/gay/pizza/pork/ffi/FfiFunctionDefinition.kt b/ffi/src/main/kotlin/gay/pizza/pork/ffi/FfiFunctionDefinition.kt new file mode 100644 index 0000000..65f1683 --- /dev/null +++ b/ffi/src/main/kotlin/gay/pizza/pork/ffi/FfiFunctionDefinition.kt @@ -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) + } + } +} diff --git a/ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaFunctionDefinition.kt b/ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaFunctionDefinition.kt new file mode 100644 index 0000000..26404b2 --- /dev/null +++ b/ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaFunctionDefinition.kt @@ -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 +) { + 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) + } + } +} diff --git a/ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaNativeProvider.kt b/ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaNativeProvider.kt new file mode 100644 index 0000000..4c42b96 --- /dev/null +++ b/ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaNativeProvider.kt @@ -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>) = 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") + } +} diff --git a/ffi/src/main/kotlin/gay/pizza/pork/ffi/JnaNativeProvider.kt b/ffi/src/main/kotlin/gay/pizza/pork/ffi/JnaNativeProvider.kt index f42cc3d..4016e30 100644 --- a/ffi/src/main/kotlin/gay/pizza/pork/ffi/JnaNativeProvider.kt +++ b/ffi/src/main/kotlin/gay/pizza/pork/ffi/JnaNativeProvider.kt @@ -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) } } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt index b516cd0..eb197b6 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt @@ -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") { @@ -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) {