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:
committed by
Space Team
parent
9a2cb2609f
commit
d18672bfb1
+6
@@ -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 {
|
||||
|
||||
+11
@@ -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"
|
||||
}
|
||||
|
||||
+24
@@ -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"
|
||||
}
|
||||
|
||||
Vendored
+21
@@ -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"
|
||||
}
|
||||
|
||||
+41
-20
@@ -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"
|
||||
}
|
||||
|
||||
+113
@@ -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"
|
||||
}
|
||||
+11
@@ -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"
|
||||
}
|
||||
|
||||
Vendored
+5
@@ -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"
|
||||
}
|
||||
|
||||
+15
-9
@@ -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"
|
||||
}
|
||||
|
||||
compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt
Vendored
+5
@@ -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"
|
||||
}
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+5
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user