package space.autistic.radio.wasm import com.dylibso.chicory.runtime.HostFunction import com.dylibso.chicory.runtime.ImportFunction import com.dylibso.chicory.runtime.Instance import com.dylibso.chicory.wasm.types.Value import com.dylibso.chicory.wasm.types.ValueType import java.lang.invoke.MethodHandles import java.lang.invoke.MethodType import java.lang.reflect.Method import java.lang.reflect.ParameterizedType class Bindings { companion object { @JvmStatic fun longToInt(long: Long): Int = long.toInt() @JvmStatic fun stringArg(instance: Instance, address: Long): String { return instance.memory().readCString(address.toInt()) } @JvmStatic fun boolArg(bool: Long): Boolean { return bool != 0L } @JvmStatic fun intListArg(instance: Instance, argc: Long, argv: Long): List { return IntArray(argc.toInt()) { instance.memory().readInt(argv.toInt() + 4 * it) }.toList() } private val lookup = MethodHandles.lookup() fun bindFunc( module: String, name: String, inLookup: MethodHandles.Lookup, method: Method, receiver: Any ): ImportFunction { val baseHandle = inLookup.unreflect(method).bindTo(receiver) val wasmParameters = ArrayList() val filters = method.genericParameterTypes.map { when (it) { Int::class.java -> { wasmParameters.add(ValueType.I32) lookup.findStatic( Bindings::class.java, "longToInt", MethodType.methodType(Int::class.java, Long::class.java) ) } Long::class.java -> { wasmParameters.add(ValueType.I64) MethodHandles.identity(Long::class.java) } Float::class.java -> { wasmParameters.add(ValueType.F32) lookup.findStatic( Value::class.java, "longToFloat", MethodType.methodType(Float::class.java, Long::class.java) ) } Double::class.java -> { wasmParameters.add(ValueType.F64) lookup.findStatic( Value::class.java, "longToDouble", MethodType.methodType(Double::class.java, Long::class.java) ) } String::class.java -> { wasmParameters.add(ValueType.I32) lookup.findStatic( Bindings::class.java, "stringArg", MethodType.methodType(String::class.java, Instance::class.java, Long::class.java) ) } Boolean::class.java -> { wasmParameters.add(ValueType.I32) lookup.findStatic( Bindings::class.java, "boolArg", MethodType.methodType(Boolean::class.java, Long::class.java) ) } is ParameterizedType -> { if (it.rawType == List::class.java) { val converter = when (it.actualTypeArguments[0]) { java.lang.Integer::class.java -> "intListArg" else -> throw IllegalArgumentException(it.actualTypeArguments[0].toString()) } wasmParameters.add(ValueType.I32) wasmParameters.add(ValueType.I32) lookup.findStatic( Bindings::class.java, converter, MethodType.methodType( List::class.java, Instance::class.java, Long::class.java, Long::class.java ) ) } else { throw IllegalArgumentException(it.toString()) } } else -> throw IllegalArgumentException(it.toString()) } } val filterTypes = ArrayList>() filters.forEach { methodHandle -> filterTypes.addAll(methodHandle.type().parameterList()) } var i = 0 val permutation = IntArray(filterTypes.size) { if (filterTypes[it] == Instance::class.java) 0 else ++i } var handle = baseHandle var j = 0 filters.forEach { handle = MethodHandles.collectArguments(handle, j, it) j += it.type().parameterCount() } val newtype = MethodType.methodType( baseHandle.type().returnType(), Instance::class.java, *Array(i) { Long::class.java }) handle = MethodHandles.permuteArguments(handle, newtype, *permutation) handle = handle.asSpreader(LongArray::class.java, i) return when (method.genericReturnType) { Void.TYPE -> HostFunction(module, name, wasmParameters, emptyList()) { instance, args -> handle.invokeExact(instance, args) Value.EMPTY_VALUES } Void::class.java -> HostFunction(module, name, wasmParameters, emptyList()) { instance, args -> handle.invokeExact(instance, args) throw IllegalStateException("unreachable") } Int::class.java -> HostFunction(module, name, wasmParameters, listOf(ValueType.I32)) { instance, args -> val result: Int = handle.invokeExact(instance, args) as Int longArrayOf(result.toLong()) } Boolean::class.java -> HostFunction( module, name, wasmParameters, listOf(ValueType.I32) ) { instance, args -> val result: Boolean = handle.invokeExact(instance, args) as Boolean longArrayOf(if (result) 1L else 0L) } Float::class.java -> HostFunction( module, name, wasmParameters, listOf(ValueType.F32) ) { instance, args -> val result: Float = handle.invokeExact(instance, args) as Float longArrayOf(Value.floatToLong(result)) } else -> throw IllegalArgumentException(method.genericReturnType.toString()) } } } }