Support non-CoroutineImpl instances in createCoroutine
This commit is contained in:
+93
@@ -0,0 +1,93 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IGNORE_BACKEND: JS
|
||||||
|
import kotlin.coroutines.experimental.*
|
||||||
|
import kotlin.coroutines.experimental.intrinsics.SUSPENDED_MARKER
|
||||||
|
|
||||||
|
fun runCustomLambdaAsCoroutine(e: Throwable? = null, x: (Continuation<String>) -> Any?): String {
|
||||||
|
var result = "fail"
|
||||||
|
var wasIntercepted = false
|
||||||
|
val c = (x as suspend () -> String).createCoroutine(object: Continuation<String> {
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
override val context: CoroutineContext
|
||||||
|
get() = object: ContinuationInterceptor {
|
||||||
|
override fun <R> fold(initial: R, operation: (R, CoroutineContext.Element) -> R): R {
|
||||||
|
throw IllegalStateException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <E : CoroutineContext.Element> get(key: CoroutineContext.Key<E>): E? {
|
||||||
|
if (key == ContinuationInterceptor.Key) {
|
||||||
|
return this as E
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T> interceptContinuation(continuation: Continuation<T>) = object : Continuation<T> {
|
||||||
|
override val context: CoroutineContext
|
||||||
|
get() = continuation.context
|
||||||
|
|
||||||
|
override fun resume(value: T) {
|
||||||
|
wasIntercepted = true
|
||||||
|
continuation.resume(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
wasIntercepted = true
|
||||||
|
continuation.resumeWithException(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext {
|
||||||
|
throw IllegalStateException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun plus(context: CoroutineContext): CoroutineContext {
|
||||||
|
throw IllegalStateException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val key: CoroutineContext.Key<*>
|
||||||
|
get() = ContinuationInterceptor.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resume(value: String) {
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (e != null)
|
||||||
|
c.resumeWithException(e)
|
||||||
|
else
|
||||||
|
c.resume(Unit)
|
||||||
|
|
||||||
|
if (!wasIntercepted) return "was not intercepted"
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val x = runCustomLambdaAsCoroutine {
|
||||||
|
it.resume("OK")
|
||||||
|
SUSPENDED_MARKER
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x != "OK") return "fail 1: $x"
|
||||||
|
|
||||||
|
val y = runCustomLambdaAsCoroutine {
|
||||||
|
"OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y != "OK") return "fail 2: $x"
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
runCustomLambdaAsCoroutine(RuntimeException("OK")) {
|
||||||
|
throw RuntimeException("fail 3")
|
||||||
|
}
|
||||||
|
} catch(e: Exception) {
|
||||||
|
return e.message!!
|
||||||
|
}
|
||||||
|
|
||||||
|
return "fail 3"
|
||||||
|
}
|
||||||
+6
@@ -4655,6 +4655,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("createCoroutinesOnManualInstances.kt")
|
||||||
|
public void testCreateCoroutinesOnManualInstances() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/createCoroutinesOnManualInstances.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("defaultParametersInSuspend.kt")
|
@TestMetadata("defaultParametersInSuspend.kt")
|
||||||
public void testDefaultParametersInSuspend() throws Exception {
|
public void testDefaultParametersInSuspend() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
||||||
|
|||||||
@@ -4655,6 +4655,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("createCoroutinesOnManualInstances.kt")
|
||||||
|
public void testCreateCoroutinesOnManualInstances() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/createCoroutinesOnManualInstances.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("defaultParametersInSuspend.kt")
|
@TestMetadata("defaultParametersInSuspend.kt")
|
||||||
public void testDefaultParametersInSuspend() throws Exception {
|
public void testDefaultParametersInSuspend() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
||||||
|
|||||||
@@ -4655,6 +4655,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("createCoroutinesOnManualInstances.kt")
|
||||||
|
public void testCreateCoroutinesOnManualInstances() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/createCoroutinesOnManualInstances.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("defaultParametersInSuspend.kt")
|
@TestMetadata("defaultParametersInSuspend.kt")
|
||||||
public void testDefaultParametersInSuspend() throws Exception {
|
public void testDefaultParametersInSuspend() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
||||||
|
|||||||
+12
@@ -5346,6 +5346,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("createCoroutinesOnManualInstances.kt")
|
||||||
|
public void testCreateCoroutinesOnManualInstances() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/createCoroutinesOnManualInstances.kt");
|
||||||
|
try {
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
catch (Throwable ignore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("defaultParametersInSuspend.kt")
|
@TestMetadata("defaultParametersInSuspend.kt")
|
||||||
public void testDefaultParametersInSuspend() throws Exception {
|
public void testDefaultParametersInSuspend() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
|
|||||||
import kotlin.coroutines.experimental.intrinsics.SUSPENDED_MARKER
|
import kotlin.coroutines.experimental.intrinsics.SUSPENDED_MARKER
|
||||||
import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
|
import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
|
||||||
import kotlin.coroutines.experimental.jvm.internal.CoroutineImpl
|
import kotlin.coroutines.experimental.jvm.internal.CoroutineImpl
|
||||||
|
import kotlin.coroutines.experimental.jvm.internal.interceptContinuationIfNeeded
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates coroutine with receiver type [R] and result type [T].
|
* Creates coroutine with receiver type [R] and result type [T].
|
||||||
@@ -35,8 +36,14 @@ import kotlin.coroutines.experimental.jvm.internal.CoroutineImpl
|
|||||||
public fun <R, T> (suspend R.() -> T).createCoroutine(
|
public fun <R, T> (suspend R.() -> T).createCoroutine(
|
||||||
receiver: R,
|
receiver: R,
|
||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Continuation<Unit> =
|
): Continuation<Unit> {
|
||||||
((this as CoroutineImpl).create(receiver, completion) as CoroutineImpl).facade
|
if (this !is CoroutineImpl) {
|
||||||
|
return buildContinuationByInvokeCall(completion) {
|
||||||
|
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ((this as CoroutineImpl).create(receiver, completion) as CoroutineImpl).facade
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts coroutine with receiver type [R] and result type [T].
|
* Starts coroutine with receiver type [R] and result type [T].
|
||||||
@@ -62,7 +69,14 @@ public fun <R, T> (suspend R.() -> T).startCoroutine(
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
public fun <T> (suspend () -> T).createCoroutine(
|
public fun <T> (suspend () -> T).createCoroutine(
|
||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Continuation<Unit> = ((this as CoroutineImpl).create(completion) as CoroutineImpl).facade
|
): Continuation<Unit> {
|
||||||
|
if (this !is CoroutineImpl) {
|
||||||
|
return buildContinuationByInvokeCall(completion) {
|
||||||
|
(this as Function1<Continuation<T>, Any?>).invoke(completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ((this as CoroutineImpl).create(completion) as CoroutineImpl).facade
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts coroutine without receiver and with result type [T].
|
* Starts coroutine without receiver and with result type [T].
|
||||||
@@ -95,6 +109,39 @@ public inline suspend fun <T> suspendCoroutine(crossinline block: (Continuation<
|
|||||||
|
|
||||||
// INTERNAL DECLARATIONS
|
// INTERNAL DECLARATIONS
|
||||||
|
|
||||||
|
private inline fun <T> buildContinuationByInvokeCall(
|
||||||
|
completion: Continuation<T>,
|
||||||
|
crossinline block: () -> Any?
|
||||||
|
): Continuation<Unit> {
|
||||||
|
val continuation =
|
||||||
|
object : Continuation<Unit> {
|
||||||
|
override val context: CoroutineContext
|
||||||
|
get() = completion.context
|
||||||
|
|
||||||
|
override fun resume(value: Unit) {
|
||||||
|
processInvokeCallOnCoroutine(completion, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
completion.resumeWithException(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return completion.context.interceptContinuationIfNeeded(continuation)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun processInvokeCallOnCoroutine(completion: Continuation<*>, block: () -> Any?) {
|
||||||
|
try {
|
||||||
|
val result = block()
|
||||||
|
if (result !== SUSPENDED_MARKER) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
(completion as Continuation<Any?>).resume(result)
|
||||||
|
}
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
completion.resumeWithException(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val UNDECIDED: Any? = Any()
|
private val UNDECIDED: Any? = Any()
|
||||||
private val RESUMED: Any? = Any()
|
private val RESUMED: Any? = Any()
|
||||||
private class Fail(val exception: Throwable)
|
private class Fail(val exception: Throwable)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import kotlin.coroutines.experimental.Continuation
|
|||||||
import kotlin.coroutines.experimental.ContinuationInterceptor
|
import kotlin.coroutines.experimental.ContinuationInterceptor
|
||||||
import kotlin.coroutines.experimental.CoroutineContext
|
import kotlin.coroutines.experimental.CoroutineContext
|
||||||
import kotlin.coroutines.experimental.intrinsics.SUSPENDED_MARKER
|
import kotlin.coroutines.experimental.intrinsics.SUSPENDED_MARKER
|
||||||
|
import kotlin.coroutines.experimental.jvm.internal.interceptContinuationIfNeeded
|
||||||
import kotlin.jvm.internal.Lambda
|
import kotlin.jvm.internal.Lambda
|
||||||
|
|
||||||
abstract class CoroutineImpl(
|
abstract class CoroutineImpl(
|
||||||
@@ -43,7 +44,7 @@ abstract class CoroutineImpl(
|
|||||||
private var _facade: Continuation<Any?>? = null
|
private var _facade: Continuation<Any?>? = null
|
||||||
|
|
||||||
val facade: Continuation<Any?> get() {
|
val facade: Continuation<Any?> get() {
|
||||||
if (_facade == null) _facade = _context!![ContinuationInterceptor]?.interceptContinuation(this) ?: this
|
if (_facade == null) _facade = _context!!.interceptContinuationIfNeeded(this)
|
||||||
return _facade!!
|
return _facade!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -19,7 +19,13 @@
|
|||||||
package kotlin.coroutines.experimental.jvm.internal
|
package kotlin.coroutines.experimental.jvm.internal
|
||||||
|
|
||||||
import kotlin.coroutines.experimental.Continuation
|
import kotlin.coroutines.experimental.Continuation
|
||||||
|
import kotlin.coroutines.experimental.CoroutineContext
|
||||||
|
import kotlin.coroutines.experimental.ContinuationInterceptor
|
||||||
import kotlin.coroutines.experimental.jvm.internal.CoroutineImpl
|
import kotlin.coroutines.experimental.jvm.internal.CoroutineImpl
|
||||||
|
|
||||||
fun <T> normalizeContinuation(continuation: Continuation<T>): Continuation<T> =
|
fun <T> normalizeContinuation(continuation: Continuation<T>): Continuation<T> =
|
||||||
(continuation as? CoroutineImpl)?.facade ?: continuation
|
(continuation as? CoroutineImpl)?.facade ?: continuation
|
||||||
|
|
||||||
|
internal fun <T> CoroutineContext.interceptContinuationIfNeeded(
|
||||||
|
continuation: Continuation<T>
|
||||||
|
) = this[ContinuationInterceptor]?.interceptContinuation(continuation) ?: continuation
|
||||||
|
|||||||
Reference in New Issue
Block a user