Return Unit manually in callSuspend and callSuspendBy if callable
returns Unit. The reason is the same as in the previous commit. If the callable is tail-call and its callee returns something other that Unit and suspends, on resume the result of the call will not be Unit. #KT-34703
This commit is contained in:
@@ -7,9 +7,11 @@
|
|||||||
package kotlin.reflect.full
|
package kotlin.reflect.full
|
||||||
|
|
||||||
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
|
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.reflect.KCallable
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.KFunction
|
||||||
import kotlin.reflect.jvm.internal.*
|
import kotlin.reflect.KParameter
|
||||||
|
import kotlin.reflect.jvm.internal.KotlinReflectionInternalError
|
||||||
|
import kotlin.reflect.jvm.internal.asKCallableImpl
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a parameter representing the `this` instance needed to call this callable,
|
* Returns a parameter representing the `this` instance needed to call this callable,
|
||||||
@@ -50,7 +52,13 @@ fun KCallable<*>.findParameterByName(name: String): KParameter? {
|
|||||||
suspend fun <R> KCallable<R>.callSuspend(vararg args: Any?): R {
|
suspend fun <R> KCallable<R>.callSuspend(vararg args: Any?): R {
|
||||||
if (!this.isSuspend) return call(*args)
|
if (!this.isSuspend) return call(*args)
|
||||||
if (this !is KFunction<*>) throw IllegalArgumentException("Cannot callSuspend on a property $this: suspend properties are not supported yet")
|
if (this !is KFunction<*>) throw IllegalArgumentException("Cannot callSuspend on a property $this: suspend properties are not supported yet")
|
||||||
return suspendCoroutineUninterceptedOrReturn { call(*args, it) }
|
val result = suspendCoroutineUninterceptedOrReturn<R> { call(*args, it) }
|
||||||
|
// If suspend function returns Unit and tail-call, it might appear, that it returns not Unit,
|
||||||
|
// see comment above replaceReturnsUnitMarkersWithPushingUnitOnStack for explanation.
|
||||||
|
// In this case, return Unit manually.
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
if (returnType.classifier == Unit::class && !returnType.isMarkedNullable) return (Unit as R)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,5 +70,11 @@ suspend fun <R> KCallable<R>.callSuspendBy(args: Map<KParameter, Any?>): R {
|
|||||||
if (!this.isSuspend) return callBy(args)
|
if (!this.isSuspend) return callBy(args)
|
||||||
if (this !is KFunction<*>) throw IllegalArgumentException("Cannot callSuspendBy on a property $this: suspend properties are not supported yet")
|
if (this !is KFunction<*>) throw IllegalArgumentException("Cannot callSuspendBy on a property $this: suspend properties are not supported yet")
|
||||||
val kCallable = asKCallableImpl() ?: throw KotlinReflectionInternalError("This callable does not support a default call: $this")
|
val kCallable = asKCallableImpl() ?: throw KotlinReflectionInternalError("This callable does not support a default call: $this")
|
||||||
return suspendCoroutineUninterceptedOrReturn<R> { kCallable.callDefaultMethod(args, it) }
|
val result = suspendCoroutineUninterceptedOrReturn<R> { kCallable.callDefaultMethod(args, it) }
|
||||||
|
// If suspend function returns Unit and tail-call, it might appear, that it returns not Unit,
|
||||||
|
// see comment above replaceReturnsUnitMarkersWithPushingUnitOnStack for explanation.
|
||||||
|
// In this case, return Unit manually.
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
if (returnType.classifier == Unit::class && !returnType.isMarkedNullable) return (Unit as R)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user