Wrap continuation with ContinuationImpl on callable references

in startCoroutineUninterceptedOrReturn. Otherwise, the coroutine will
not be interceptable later.

Add a test, which checks, that intercepted continuation is released.

 #KT-55869
This commit is contained in:
Ilmir Usmanov
2023-01-17 05:48:12 +01:00
committed by Space Team
parent 9a2cb2609f
commit d18672bfb1
23 changed files with 413 additions and 39 deletions
@@ -12546,6 +12546,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@Test
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@Test
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
@@ -55,6 +55,17 @@ fun box(): String {
if (res != "OK") {
return "fail 3 $res"
}
res = builder(::suspendHere)
if (res != "OK") {
return "fail 4 $res"
}
res = builder {
suspend {}()
suspendHere()
}
if (res != "OK") {
return "fail 5 $res"
}
return "OK"
}
@@ -56,6 +56,30 @@ fun box(): String {
if (res != "OK") {
return "fail 3 $res"
}
res = c.builder {
suspend {}()
controllerSuspendHere()
}
if (res != "OK") {
return "fail 4 $res"
}
res = c.builder {
suspend {}()
controllerMultipleArgs(1, 1, 1)
}
if (res != "OK") {
return "fail 5 $res"
}
res = c.builder {
suspend {}()
if (coroutineContext != EmptyCoroutineContext)
"${coroutineContext} != $EmptyCoroutineContext"
else
"OK"
}
if (res != "OK") {
return "fail 6 $res"
}
return "OK"
}
@@ -55,6 +55,27 @@ fun box(): String {
if (res != "OK") {
return "fail 3 $res"
}
res = v.builder {
suspend {}()
controllerMultipleArgs(1, 1, 1)
}
if (res != "OK") {
return "fail 4 $res"
}
res = v.builder {
suspend {}()
if (coroutineContext == EmptyCoroutineContext) "$coroutineContext == $EmptyCoroutineContext" else "OK"
}
if (res != "OK") {
return "fail 5 $res"
}
res = v.builder {
suspend {}()
controllerSuspendHere()
}
if (res != "OK") {
return "fail 6 $res"
}
return "OK"
}
@@ -1,5 +1,6 @@
// WITH_STDLIB
// WITH_COROUTINES
// IGNORE_BACKEND: JS_IR, NATIVE
import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
@@ -25,28 +26,24 @@ suspend fun suspendWithExceptionIntercepted(): String = suspendCoroutineUninterc
COROUTINE_SUSPENDED
}
fun builder(expectedCount: Int, c: suspend () -> String): String {
fun builder(testNum: Int, expectedCount: Int, c: suspend () -> String): String {
var fromSuspension: String? = null
var counter = 0
val result = try {
c.startCoroutineUninterceptedOrReturn(object: ContinuationAdapter<String>() {
override val context: CoroutineContext
get() = ContinuationDispatcher { counter++ }
c.startCoroutineUninterceptedOrReturn(object : ContinuationAdapter<String>() {
override val context: CoroutineContext
get() = ContinuationDispatcher { counter++ }
override fun resumeWithException(exception: Throwable) {
fromSuspension = "Exception: " + exception.message!!
}
override fun resumeWithException(exception: Throwable) {
fromSuspension = "Exception: " + exception.message!!
}
override fun resume(value: String) {
fromSuspension = value
}
})
} catch (e: Exception) {
"Exception: ${e.message}"
}
override fun resume(value: String) {
fromSuspension = value
}
})
if (counter != expectedCount) throw RuntimeException("fail 0")
if (counter != expectedCount) throw RuntimeException("fail 0 $testNum $counter != $expectedCount")
return fromSuspension!!
}
@@ -72,11 +69,35 @@ private class DispatchedContinuation<T>(
}
fun box(): String {
if (builder(0) { suspendHereUnintercepted() } != "OK") return "fail 2"
if (builder(1) { suspendHereIntercepted() } != "OK") return "fail 3"
if (builder(0, 0) { suspendHereUnintercepted() } != "OK") return "fail 2"
if (builder(1, 1) { suspendHereIntercepted() } != "OK") return "fail 3"
if (builder(0) { suspendWithExceptionUnintercepted() } != "Exception: OK") return "fail 4"
if (builder(1) { suspendWithExceptionIntercepted() } != "Exception: OK") return "fail 5"
if (builder(2, 0) { suspendWithExceptionUnintercepted() } != "Exception: OK") return "fail 4"
if (builder(3, 1) { suspendWithExceptionIntercepted() } != "Exception: OK") return "fail 5"
if (builder(4, 0, ::suspendHereUnintercepted) != "OK") return "fail 6"
if (builder(5, 1, ::suspendHereIntercepted) != "OK") return "fail 7"
if (builder(6, 0, ::suspendWithExceptionUnintercepted) != "Exception: OK") return "fail 8"
if (builder(7, 1, ::suspendWithExceptionIntercepted) != "Exception: OK") return "fail 9"
if (builder(8, 0) {
suspend {}()
suspendHereUnintercepted()
} != "OK") return "fail 21"
if (builder(9, 1) {
suspend {}()
suspendHereIntercepted()
} != "OK") return "fail 31"
if (builder(10, 0) {
suspend {}()
suspendWithExceptionUnintercepted()
} != "Exception: OK") return "fail 41"
if (builder(11, 1) {
suspend {}()
suspendWithExceptionIntercepted()
} != "Exception: OK") return "fail 51"
return "OK"
}
@@ -0,0 +1,113 @@
// WITH_STDLIB
// IGNORE_BACKEND: JS_IR, NATIVE
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
var c: Continuation<String>? = null
var interceptions: Int = 0
var releases: Int = 0
var invocations: Int = 0
suspend fun suspendHere(): String = suspendCoroutineUninterceptedOrReturn { x ->
invocations++
c = x
COROUTINE_SUSPENDED
}
suspend fun suspendHereIntercepted(): String = suspendCoroutineUninterceptedOrReturn { x ->
invocations++
c = x.intercepted()
COROUTINE_SUSPENDED
}
fun builder(testNum: Int, expectedResult: Result<String>, c: suspend () -> String) {
c.startCoroutineUninterceptedOrReturn(object : Continuation<String> {
override val context: CoroutineContext
get() = ContinuationDispatcher()
override fun resumeWith(result: Result<String>) {
if (result != expectedResult) {
error("fail $testNum: unexpected result: $expectedResult != $result")
}
}
})
}
class ContinuationDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = DispatchedContinuation(continuation)
override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
releases++
}
}
private class DispatchedContinuation<T>(
val continuation: Continuation<T>
): Continuation<T> {
init {
interceptions++
}
override val context: CoroutineContext = continuation.context
override fun resumeWith(result: Result<T>) {
continuation.resumeWith(result)
}
}
fun box(): String {
val success = Result.success("OK")
builder(0, success) { suspendHere() }
if (invocations != 1) return "fail 00: $invocations"
if (interceptions != 0) return "fail 01: $interceptions"
if (releases != 0) return "fail 02: $releases"
c = c?.intercepted()
if (interceptions != 1) return "fail 03: $interceptions"
c?.resumeWith(success)
if (releases != 1) return "fail 04: $releases"
builder(1, success) { suspendHereIntercepted() }
if (invocations != 2) return "fail 10: $invocations"
if (interceptions != 2) return "fail 11: $interceptions"
if (releases != 1) return "fail 12: $interceptions"
c?.resumeWith(success)
if (interceptions != 2) return "fail 13: $interceptions"
if (releases != 2) return "fail 14: $releases"
builder(2, success, ::suspendHere)
if (invocations != 3) return "fail 20: $invocations"
if (interceptions != 2) return "fail 21: $interceptions"
if (releases != 2) return "fail 22: $releases"
c = c?.intercepted()
if (interceptions != 3) return "fail 23: $interceptions"
c?.resumeWith(success)
if (releases != 3) return "fail 24: $releases"
builder(3, success, ::suspendHereIntercepted)
if (invocations != 4) return "fail 30: $invocations"
if (interceptions != 4) return "fail 31: $interceptions"
if (releases != 3) return "fail 32: $interceptions"
c?.resumeWith(success)
if (interceptions != 4) return "fail 33: $interceptions"
if (releases != 4) return "fail 34: $releases"
builder(4, success) { suspend {}(); suspendHere() }
if (invocations != 5) return "fail 40: $invocations"
if (interceptions != 4) return "fail 41: $interceptions"
if (releases != 4) return "fail 42: $releases"
c = c?.intercepted()
if (interceptions != 5) return "fail 43: $interceptions"
c?.resumeWith(success)
if (releases != 5) return "fail 44: $releases"
builder(5, success) { suspend {}(); suspendHereIntercepted() }
if (invocations != 6) return "fail 50: $invocations"
if (interceptions != 6) return "fail 51: $interceptions"
if (releases != 5) return "fail 52: $interceptions"
c?.resumeWith(success)
if (interceptions != 6) return "fail 53: $interceptions"
if (releases != 6) return "fail 54: $releases"
return "OK"
}
@@ -37,9 +37,20 @@ fun builder(c: suspend () -> String): String {
fun box(): String {
if (builder { "OK" } != "OK") return "fail 4"
if (builder { suspendHere() } != "OK") return "fail 5"
if (builder {
suspend {}()
suspendHere()
} != "OK") return "fail 51"
if (builder { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
if (builder { suspendWithException() } != "Exception: OK") return "fail 7"
if (builder {
suspend {}()
suspendWithException()
} != "Exception: OK") return "fail 71"
if (builder(::suspendHere) != "OK") return "fail 8"
if (builder(::suspendWithException) != "Exception: OK") return "fail 9"
return "OK"
}
@@ -48,9 +48,14 @@ fun builder(shouldSuspend: Boolean, c: suspend () -> String): String {
fun box(): String {
if (builder(false) { "OK" } != "OK") return "fail 4"
if (builder(true) { suspendHere() } != "OK") return "fail 5"
if (builder(true) { suspend{}(); suspendHere() } != "OK") return "fail 51"
if (builder(false) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
if (builder(true) { suspendWithException() } != "Exception: OK") return "fail 7"
if (builder(true) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71"
if (builder(true, ::suspendHere) != "OK") return "fail 8"
if (builder(true, ::suspendWithException) != "Exception: OK") return "fail 9"
return "OK"
}
@@ -1,5 +1,6 @@
// WITH_STDLIB
// WITH_COROUTINES
// IGNORE_BACKEND: NATIVE
import helpers.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*
@@ -19,7 +20,7 @@ suspend fun suspendWithException(): String = suspendCoroutine { x ->
}
}
fun builder(shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String): String {
fun builder(testNum: Int, shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String): String {
callback = {}
var fromSuspension: String? = null
var counter = 0
@@ -43,15 +44,15 @@ fun builder(shouldSuspend: Boolean, expectedCount: Int, c: suspend () -> String)
callback()
if (counter != expectedCount) throw RuntimeException("fail 0")
if (counter != expectedCount) throw RuntimeException("fail 0 $testNum $counter != $expectedCount")
if (shouldSuspend) {
if (result !== COROUTINE_SUSPENDED) throw RuntimeException("fail 1")
if (fromSuspension == null) throw RuntimeException("fail 2")
if (result !== COROUTINE_SUSPENDED) throw RuntimeException("fail 1 $testNum $result")
if (fromSuspension == null) throw RuntimeException("fail 2 $testNum")
return fromSuspension!!
}
if (result === COROUTINE_SUSPENDED) throw RuntimeException("fail 3")
if (result === COROUTINE_SUSPENDED) throw RuntimeException("fail 3 $testNum")
return result as String
}
@@ -77,11 +78,16 @@ private class DispatchedContinuation<T>(
}
fun box(): String {
if (builder(false, 0) { "OK" } != "OK") return "fail 4"
if (builder(true, 1) { suspendHere() } != "OK") return "fail 5"
if (builder(0, false, 0) { "OK" } != "OK") return "fail 4"
if (builder(1, true, 1) { suspendHere() } != "OK") return "fail 5"
if (builder(2, true, 1) { suspend{}(); suspendHere() } != "OK") return "fail 51"
if (builder(false, 0) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
if (builder(true, 1) { suspendWithException() } != "Exception: OK") return "fail 7"
if (builder(3, false, 0) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
if (builder(4, true, 1) { suspendWithException() } != "Exception: OK") return "fail 7"
if (builder(5, true, 1) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71"
if (builder(6, true, 1, ::suspendHere) != "OK") return "fail 8"
if (builder(7, true, 1, ::suspendWithException) != "Exception: OK") return "fail 9"
return "OK"
}
@@ -72,9 +72,14 @@ private class DispatchedContinuation<T>(
fun box(): String {
if (builder(false) { "OK" } != "OK") return "fail 4"
if (builder(true) { suspendHere() } != "OK") return "fail 5"
if (builder(true) { suspend{}(); suspendHere() } != "OK") return "fail 51"
if (builder(false) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
if (builder(true) { suspendWithException() } != "Exception: OK") return "fail 7"
if (builder(true) { suspend{}(); suspendWithException() } != "Exception: OK") return "fail 71"
if (builder(true, ::suspendHere) != "OK") return "fail 8"
if (builder(true, ::suspendWithException) != "Exception: OK") return "fail 9"
return "OK"
}
@@ -12336,6 +12336,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@Test
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@Test
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
@@ -12546,6 +12546,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@Test
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@Test
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
@@ -9875,6 +9875,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/resultExceptionOrNullInLambda.kt");
@@ -9158,6 +9158,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@Test
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@Test
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
@@ -9254,6 +9254,12 @@ public class FirJsCodegenBoxTestGenerated extends AbstractFirJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@Test
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@Test
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
@@ -9254,6 +9254,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@Test
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@Test
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
@@ -8135,6 +8135,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/resultExceptionOrNullInLambda.kt");
@@ -24,7 +24,7 @@ import kotlin.internal.InlineOnly
@InlineOnly
public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
completion: Continuation<T>
): Any? = this.asDynamic()(completion, false)
): Any? = this.asDynamic()(if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion, false)
/**
* Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
@@ -43,14 +43,18 @@ public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrRetu
public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
completion: Continuation<T>
): Any? = this.asDynamic()(receiver, completion, false)
): Any? = this.asDynamic()(
receiver, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion, false
)
@InlineOnly
internal actual inline fun <R, P, T> (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
param: P,
completion: Continuation<T>
): Any? = this.asDynamic()(receiver, param, completion, false)
): Any? = this.asDynamic()(
receiver, param, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion, false
)
/**
* Creates unintercepted coroutine without receiver and with result type [T].
@@ -149,3 +153,13 @@ private inline fun <T> createCoroutineFromSuspendFunction(
}
}
}
@PublishedApi
internal fun <T> createSimpleCoroutineFromSuspendFunction(
completion: Continuation<T>
): CoroutineImpl = object : CoroutineImpl(completion as Continuation<Any?>) {
override fun doResume(): Any? {
if (exception != null) throw exception as Throwable
return result
}
}
@@ -29,7 +29,19 @@ import kotlin.internal.InlineOnly
@InlineOnly
public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
completion: Continuation<T>
): Any? = (this as Function1<Continuation<T>, Any?>).invoke(completion)
): Any? =
// Wrap with ContinuationImpl, otherwise the coroutine will not be interceptable. See KT-55869
if (this !is BaseContinuationImpl) wrapWithContinuationImpl(completion)
else (this as Function1<Continuation<T>, Any?>).invoke(completion)
// Work around private and internal visibilities of functions used: [createCoroutineFromSuspendFunction] and [probeCoroutineCreated].
@PublishedApi
internal fun <T> (suspend () -> T).wrapWithContinuationImpl(
completion: Continuation<T>
): Any? {
val newCompletion = createSimpleCoroutineForSuspendFunction(probeCoroutineCreated(completion))
return (this as Function1<Continuation<T>, Any?>).invoke(newCompletion)
}
/**
* Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
@@ -48,14 +60,41 @@ public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrRetu
public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
completion: Continuation<T>
): Any? = (this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion)
): Any? =
// Wrap with ContinuationImpl, otherwise the coroutine will not be interceptable. See KT-55869
if (this !is BaseContinuationImpl) wrapWithContinuationImpl(receiver, completion)
else (this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion)
// Work around private and internal visibilities of functions used: [createCoroutineFromSuspendFunction] and [probeCoroutineCreated].
@PublishedApi
internal fun <R, T> (suspend R.() -> T).wrapWithContinuationImpl(
receiver: R,
completion: Continuation<T>
): Any? {
val newCompletion = createSimpleCoroutineForSuspendFunction(probeCoroutineCreated(completion))
return (this as Function2<R, Continuation<T>, Any?>).invoke(receiver, newCompletion)
}
@InlineOnly
internal actual inline fun <R, P, T> (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
param: P,
completion: Continuation<T>
): Any? = (this as Function3<R, P, Continuation<T>, Any?>).invoke(receiver, param, completion)
): Any? =
// Wrap with ContinuationImpl, otherwise the coroutine will not be interceptable. See KT-55869
if (this !is BaseContinuationImpl) wrapWithContinuationImpl(receiver, param, completion)
else (this as Function3<R, P, Continuation<T>, Any?>).invoke(receiver, param, completion)
// Work around private and internal visibilities of functions used: [createCoroutineFromSuspendFunction] and [probeCoroutineCreated].
@PublishedApi
internal fun <R, P, T> (suspend R.(P) -> T).wrapWithContinuationImpl(
receiver: R,
param: P,
completion: Continuation<T>
): Any? {
val newCompletion = createSimpleCoroutineForSuspendFunction(probeCoroutineCreated(completion))
return (this as Function3<R, P, Continuation<T>, Any?>).invoke(receiver, param, newCompletion)
}
// JVM declarations
@@ -201,3 +240,30 @@ private inline fun <T> createCoroutineFromSuspendFunction(
}
}
}
/**
* This function is used when [startCoroutineUninterceptedOrReturn] encounters suspending lambda that does not extend BaseContinuationImpl.
*
* It happens in two cases:
* 1. Callable reference to suspending function or tail-call lambdas,
* 2. Suspending function reference implemented by Java code.
*
* This function is the same as above, but does not run lambda itself - the caller is expected to call [invoke] manually.
*/
private fun <T> createSimpleCoroutineForSuspendFunction(
completion: Continuation<T>
): Continuation<T> {
val context = completion.context
return if (context === EmptyCoroutineContext)
object : RestrictedContinuationImpl(completion as Continuation<Any?>) {
override fun invokeSuspend(result: Result<Any?>): Any? {
return result.getOrThrow()
}
}
else
object : ContinuationImpl(completion as Continuation<Any?>, context) {
override fun invokeSuspend(result: Result<Any?>): Any? {
return result.getOrThrow()
}
}
}
@@ -24,7 +24,9 @@ import kotlin.wasm.internal.*
@kotlin.internal.InlineOnly
public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
completion: Continuation<T>
): Any? = startCoroutineUninterceptedOrReturnIntrinsic0(this, completion)
): Any? = startCoroutineUninterceptedOrReturnIntrinsic0(
this, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion
)
/**
* Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
@@ -43,7 +45,9 @@ public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrRetu
public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
completion: Continuation<T>
): Any? = startCoroutineUninterceptedOrReturnIntrinsic1(this, receiver, completion)
): Any? = startCoroutineUninterceptedOrReturnIntrinsic1(
this, receiver, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion
)
@Suppress("UNCHECKED_CAST")
@kotlin.internal.InlineOnly
@@ -51,7 +55,9 @@ internal actual inline fun <R, P, T> (suspend R.(P) -> T).startCoroutineUninterc
receiver: R,
param: P,
completion: Continuation<T>
): Any? = startCoroutineUninterceptedOrReturnIntrinsic2(this, receiver, param, completion)
): Any? = startCoroutineUninterceptedOrReturnIntrinsic2(
this, receiver, param, if (this !is CoroutineImpl) createSimpleCoroutineFromSuspendFunction(completion) else completion
)
/**
* Creates unintercepted coroutine without receiver and with result type [T].
@@ -137,4 +143,14 @@ private inline fun <T> createCoroutineFromSuspendFunction(
return block()
}
}
}
}
@PublishedApi
internal fun <T> createSimpleCoroutineFromSuspendFunction(
completion: Continuation<T>
): CoroutineImpl = object : CoroutineImpl(completion as Continuation<Any?>) {
override fun doResume(): Any? {
if (exception != null) throw exception as Throwable
return result
}
}
@@ -3090,6 +3090,9 @@ public final class kotlin/coroutines/intrinsics/IntrinsicsKt {
public static final fun createCoroutineUnintercepted (Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
public static final fun getCOROUTINE_SUSPENDED ()Ljava/lang/Object;
public static final fun intercepted (Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
public static final fun wrapWithContinuationImpl (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun wrapWithContinuationImpl (Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun wrapWithContinuationImpl (Lkotlin/jvm/functions/Function3;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class kotlin/coroutines/jvm/internal/Boxing {
@@ -10259,6 +10259,12 @@ public class K2NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTes
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@Test
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@Test
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {
@@ -10152,6 +10152,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
}
@Test
@TestMetadata("releaseIntercepted.kt")
public void testReleaseIntercepted() throws Exception {
runTest("compiler/testData/codegen/box/coroutines/intrinsicSemantics/releaseIntercepted.kt");
}
@Test
@TestMetadata("resultExceptionOrNullInLambda.kt")
public void testResultExceptionOrNullInLambda() throws Exception {