mirror of
				https://github.com/GayPizzaSpecifications/pork.git
				synced 2025-11-03 17:39:38 +00:00 
			
		
		
		
	ffi: support for java native functions
This commit is contained in:
		
							
								
								
									
										10
									
								
								examples/java.pork
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								examples/java.pork
									
									
									
									
									
										Normal file
									
								
							@ -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")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -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)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -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
									
								
								ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaNativeProvider.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaNativeProvider.kt
									
									
									
									
									
										Normal file
									
								
							@ -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")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -6,11 +6,10 @@ import gay.pizza.pork.evaluator.NativeFunctionProvider
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class JnaNativeProvider : NativeFunctionProvider {
 | 
					class JnaNativeProvider : NativeFunctionProvider {
 | 
				
			||||||
  override fun provideNativeFunction(definition: String): CallableFunction {
 | 
					  override fun provideNativeFunction(definition: String): CallableFunction {
 | 
				
			||||||
    val (libraryName, functionSymbol, returnType, _) =
 | 
					    val functionDefinition = FfiFunctionDefinition.parse(definition)
 | 
				
			||||||
      definition.split(":", limit = 3)
 | 
					    val function = Function.getFunction(functionDefinition.library, functionDefinition.function)
 | 
				
			||||||
    val function = Function.getFunction(libraryName, functionSymbol)
 | 
					 | 
				
			||||||
    return CallableFunction {
 | 
					    return CallableFunction {
 | 
				
			||||||
      return@CallableFunction invoke(function, it.values.toTypedArray(), returnType)
 | 
					      return@CallableFunction invoke(function, it.values.toTypedArray(), functionDefinition.returnType)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ import gay.pizza.pork.evaluator.Arguments
 | 
				
			|||||||
import gay.pizza.pork.evaluator.CallableFunction
 | 
					import gay.pizza.pork.evaluator.CallableFunction
 | 
				
			||||||
import gay.pizza.pork.evaluator.None
 | 
					import gay.pizza.pork.evaluator.None
 | 
				
			||||||
import gay.pizza.pork.evaluator.Scope
 | 
					import gay.pizza.pork.evaluator.Scope
 | 
				
			||||||
 | 
					import gay.pizza.pork.ffi.JavaNativeProvider
 | 
				
			||||||
import gay.pizza.pork.ffi.JnaNativeProvider
 | 
					import gay.pizza.pork.ffi.JnaNativeProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RunCommand : CliktCommand(help = "Run Program", name = "run") {
 | 
					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 = {
 | 
					    val main = tool.loadMainFunction(scope, setupEvaluator = {
 | 
				
			||||||
      addNativeFunctionProvider("ffi", JnaNativeProvider())
 | 
					      addNativeFunctionProvider("ffi", JnaNativeProvider())
 | 
				
			||||||
 | 
					      addNativeFunctionProvider("java", JavaNativeProvider())
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    maybeLoopAndMeasure(loop, measure) {
 | 
					    maybeLoopAndMeasure(loop, measure) {
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user