All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.ch.softappeal.yass2.InterceptorReflection.kt Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
package ch.softappeal.yass2

import java.lang.reflect.*
import java.lang.reflect.Proxy.*
import kotlin.coroutines.*
import kotlin.reflect.*
import kotlin.reflect.full.*
import kotlin.reflect.jvm.*

internal fun KClass<*>.serviceFunctions() = memberFunctions
    .filter { it.javaMethod!!.declaringClass != Object::class.java }
    .sortedBy { it.name }

internal fun KClass<*>.checkService() {
    val sf = serviceFunctions()
    sf.forEach { require(it.isSuspend) { "'$it' is not a suspend function" } }
    require(sf.map { it.name }.toSet().size == sf.size) { "'$this' has overloaded functions" }
}

private interface SuspendFunction {
    suspend fun invoke(): Any?
}

private val SuspendMethod = SuspendFunction::class.java.methods[0]

internal fun invokeSuspendFunction(continuation: Continuation<*>, suspendFunction: suspend () -> Any?) = try {
    SuspendMethod.invoke(
        object : SuspendFunction {
            override suspend fun invoke() = suspendFunction()
        },
        continuation
    )
} catch (e: InvocationTargetException) {
    throw e.cause!!
}

object ReflectionProxyFactory : ProxyFactory {
    override fun  create(service: KClass, implementation: S, interceptor: Interceptor): S {
        service.checkService()
        @Suppress("UNCHECKED_CAST")
        return newProxyInstance(service.java.classLoader, arrayOf(service.java)) { _, method, args ->
            val parameters = args.copyOf(args.size - 1)
            val continuation = args.last() as Continuation<*>
            invokeSuspendFunction(continuation) {
                interceptor(method.name, parameters) {
                    try {
                        method.invoke(implementation, *parameters, continuation)
                    } catch (e: InvocationTargetException) {
                        throw e.cause!!
                    }
                }
            }
        } as S
    }
}