package io.aconite.utils

import java.lang.reflect.InvocationTargetException
import kotlin.coroutines.experimental.Continuation
import kotlin.coroutines.experimental.CoroutineContext
import kotlin.coroutines.experimental.suspendCoroutine
import kotlin.reflect.KFunction

/**
 * This object can be used as the return value of the async function to indicate
 * that function was suspended. This works only with function [asyncCall].
 */
val COROUTINE_SUSPENDED = Any()

private class MyContinuation<in R>(val c: Continuation<R>): Continuation<R> {
    override val context: CoroutineContext
        get() = c.context

    override fun resume(value: R) {
        if (value === COROUTINE_SUSPENDED) return
        c.resume(value)
    }

    override fun resumeWithException(exception: Throwable) {
        if (exception === COROUTINE_SUSPENDED) return
        c.resumeWithException(exception)
    }
}

/**
 * Extension for calling asynchronous functions by reflection.
 * @receiver the called function
 * @param[args] arguments of the called function
 * @return result of the called function
 */
suspend fun <R> KFunction<R>.asyncCall(vararg args: Any?) = suspendCoroutine<R> { c ->
    val cc = MyContinuation(c)
    try {
        val r = call(*args, cc)
        cc.resume(r)
    } catch (ex: InvocationTargetException) {
        throw ex.cause ?: ex
    }
}
package io.aconite.utils

import java.lang.reflect.InvocationTargetException
import kotlin.coroutines.experimental.suspendCoroutine
import kotlin.reflect.KFunction

/**
 * This object can be used as the return value of the async function to indicate
 * that function was suspended.
 * TODO: find better way to use suspend with reflection
 */
val COROUTINE_SUSPENDED: Any = {
    val cls = Class.forName("kotlin.coroutines.experimental.intrinsics.IntrinsicsKt")
    val field = cls.getDeclaredField("COROUTINE_SUSPENDED")
    field.isAccessible = true
    field.get(null)
}()

/**
 * Extension for calling asynchronous functions by reflection.
 * @receiver the called function
 * @param[args] arguments of the called function
 * @return result of the called function
 */
suspend fun <R> KFunction<R>.asyncCall(vararg args: Any?) = suspendCoroutine<R> { c ->
    try {
        val r = call(*args, c)
        if (r !== COROUTINE_SUSPENDED) c.resume(r)
    } catch (ex: InvocationTargetException) {
        throw ex.cause ?: ex
    }
}
Use of COROUTINE_SUSPENDED from Kotlin internals
Use of COROUTINE_SUSPENDED from Kotlin internals

AcapellaSoft/Aconite,AcapellaSoft/Aconite