Move release coroutines sources into the corresponding stdlib sourcesets
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package kotlin.coroutines
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
|
||||
import kotlin.coroutines.intrinsics.CoroutineSingletons.*
|
||||
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||
import kotlin.coroutines.jvm.internal.CoroutineStackFrame
|
||||
|
||||
@PublishedApi
|
||||
@SinceKotlin("1.3")
|
||||
internal actual class SafeContinuation<in T>
|
||||
internal actual constructor(
|
||||
private val delegate: Continuation<T>,
|
||||
initialResult: Any?
|
||||
) : Continuation<T>, CoroutineStackFrame {
|
||||
@PublishedApi
|
||||
internal actual constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
|
||||
|
||||
public actual override val context: CoroutineContext
|
||||
get() = delegate.context
|
||||
|
||||
@Volatile
|
||||
private var result: Any? = initialResult
|
||||
|
||||
private companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@JvmStatic
|
||||
private val RESULT = AtomicReferenceFieldUpdater.newUpdater<SafeContinuation<*>, Any?>(
|
||||
SafeContinuation::class.java, Any::class.java as Class<Any?>, "result"
|
||||
)
|
||||
}
|
||||
|
||||
public actual override fun resumeWith(result: Result<T>) {
|
||||
while (true) { // lock-free loop
|
||||
val cur = this.result // atomic read
|
||||
when {
|
||||
cur === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, result.value)) return
|
||||
cur === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)) {
|
||||
delegate.resumeWith(result)
|
||||
return
|
||||
}
|
||||
else -> throw IllegalStateException("Already resumed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal actual fun getOrThrow(): Any? {
|
||||
var result = this.result // atomic read
|
||||
if (result === UNDECIDED) {
|
||||
if (RESULT.compareAndSet(this, UNDECIDED, COROUTINE_SUSPENDED)) return COROUTINE_SUSPENDED
|
||||
result = this.result // reread volatile var
|
||||
}
|
||||
return when {
|
||||
result === RESUMED -> COROUTINE_SUSPENDED // already called continuation, indicate COROUTINE_SUSPENDED upstream
|
||||
result is Result.Failure -> throw result.exception
|
||||
else -> result // either COROUTINE_SUSPENDED or data
|
||||
}
|
||||
}
|
||||
|
||||
// --- CoroutineStackFrame implementation
|
||||
|
||||
public override val callerFrame: CoroutineStackFrame?
|
||||
get() = delegate as? CoroutineStackFrame
|
||||
|
||||
override fun getStackTraceElement(): StackTraceElement? =
|
||||
null
|
||||
|
||||
override fun toString(): String =
|
||||
"SafeContinuation for $delegate"
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:kotlin.jvm.JvmName("IntrinsicsKt")
|
||||
@file:kotlin.jvm.JvmMultifileClass
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package kotlin.coroutines.intrinsics
|
||||
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.jvm.internal.*
|
||||
import kotlin.internal.InlineOnly
|
||||
|
||||
/**
|
||||
* Starts unintercepted coroutine without receiver and with result type [T] and executes it until its first suspension.
|
||||
* Returns the result of the coroutine or throws its exception if it does not suspend or [COROUTINE_SUSPENDED] if it suspends.
|
||||
* In the later case, the [completion] continuation is invoked when coroutine completes with result or exception.
|
||||
*
|
||||
* The coroutine is started directly in the invoker's thread without going through the [ContinuationInterceptor] that might
|
||||
* be present in the completion's [CoroutineContext]. It is invoker's responsibility to ensure that the proper invocation
|
||||
* context is established.
|
||||
*
|
||||
* This function is designed to be used from inside of [suspendCoroutineUninterceptedOrReturn] to resume the execution of suspended
|
||||
* coroutine using a reference to the suspending function.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@InlineOnly
|
||||
public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
|
||||
completion: Continuation<T>
|
||||
): Any? = (this as Function1<Continuation<T>, Any?>).invoke(completion)
|
||||
|
||||
/**
|
||||
* Starts unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
|
||||
* Returns the result of the coroutine or throws its exception if it does not suspend or [COROUTINE_SUSPENDED] if it suspends.
|
||||
* In the later case, the [completion] continuation is invoked when coroutine completes with result or exception.
|
||||
*
|
||||
* The coroutine is started directly in the invoker's thread without going through the [ContinuationInterceptor] that might
|
||||
* be present in the completion's [CoroutineContext]. It is invoker's responsibility to ensure that the proper invocation
|
||||
* context is established.
|
||||
*
|
||||
* This function is designed to be used from inside of [suspendCoroutineUninterceptedOrReturn] to resume the execution of suspended
|
||||
* coroutine using a reference to the suspending function.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@InlineOnly
|
||||
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)
|
||||
|
||||
|
||||
// JVM declarations
|
||||
|
||||
/**
|
||||
* Creates unintercepted coroutine without receiver and with result type [T].
|
||||
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
||||
*
|
||||
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
|
||||
* The [completion] continuation is invoked when coroutine completes with result or exception.
|
||||
*
|
||||
* This function returns unintercepted continuation.
|
||||
* Invocation of `resume(Unit)` starts coroutine immediately in the invoker's call stack without going through the
|
||||
* [ContinuationInterceptor] that might be present in the completion's [CoroutineContext].
|
||||
* It is invoker's responsibility to ensure that the proper invocation context is established.
|
||||
* Note that [completion] of this function may get invoked in an arbitrary context.
|
||||
*
|
||||
* [Continuation.intercepted] can be used to acquire the intercepted continuation.
|
||||
* Invocation of `resume(Unit)` on intercepted continuation guarantees that execution of
|
||||
* both the coroutine and [completion] happens in the invocation context established by
|
||||
* [ContinuationInterceptor].
|
||||
*
|
||||
* Repeated invocation of any resume function on the resulting continuation corrupts the
|
||||
* state machine of the coroutine and may result in arbitrary behaviour or exception.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
|
||||
completion: Continuation<T>
|
||||
): Continuation<Unit> {
|
||||
val probeCompletion = probeCoroutineCreated(completion)
|
||||
return if (this is BaseContinuationImpl)
|
||||
create(probeCompletion)
|
||||
else
|
||||
createCoroutineFromSuspendFunction(probeCompletion) {
|
||||
(this as Function1<Continuation<T>, Any?>).invoke(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates unintercepted coroutine with receiver type [R] and result type [T].
|
||||
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
||||
*
|
||||
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
|
||||
* The [completion] continuation is invoked when coroutine completes with result or exception.
|
||||
*
|
||||
* This function returns unintercepted continuation.
|
||||
* Invocation of `resume(Unit)` starts coroutine immediately in the invoker's call stack without going through the
|
||||
* [ContinuationInterceptor] that might be present in the completion's [CoroutineContext].
|
||||
* It is invoker's responsibility to ensure that the proper invocation context is established.
|
||||
* Note that [completion] of this function may get invoked in an arbitrary context.
|
||||
*
|
||||
* [Continuation.intercepted] can be used to acquire the intercepted continuation.
|
||||
* Invocation of `resume(Unit)` on intercepted continuation guarantees that execution of
|
||||
* both the coroutine and [completion] happens in the invocation context established by
|
||||
* [ContinuationInterceptor].
|
||||
*
|
||||
* Repeated invocation of any resume function on the resulting continuation corrupts the
|
||||
* state machine of the coroutine and may result in arbitrary behaviour or exception.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
|
||||
receiver: R,
|
||||
completion: Continuation<T>
|
||||
): Continuation<Unit> {
|
||||
val probeCompletion = probeCoroutineCreated(completion)
|
||||
return if (this is BaseContinuationImpl)
|
||||
create(receiver, probeCompletion)
|
||||
else {
|
||||
createCoroutineFromSuspendFunction(probeCompletion) {
|
||||
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts this continuation with [ContinuationInterceptor].
|
||||
*
|
||||
* This function shall be used on the immediate result of [createCoroutineUnintercepted] or [suspendCoroutineUninterceptedOrReturn],
|
||||
* in which case it checks for [ContinuationInterceptor] in the continuation's [context][Continuation.context],
|
||||
* invokes [ContinuationInterceptor.interceptContinuation], caches and returns result.
|
||||
*
|
||||
* If this function is invoked on other [Continuation] instances it returns `this` continuation unchanged.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
|
||||
(this as? ContinuationImpl)?.intercepted() ?: this
|
||||
|
||||
// INTERNAL DEFINITIONS
|
||||
|
||||
/**
|
||||
* This function is used when [createCoroutineUnintercepted] encounters suspending lambda that does not extend BaseContinuationImpl.
|
||||
*
|
||||
* It happens in two cases:
|
||||
* 1. Callable reference to suspending function,
|
||||
* 2. Suspending function reference implemented by Java code.
|
||||
*
|
||||
* We must wrap it into an instance that extends [BaseContinuationImpl], because that is an expectation of all coroutines machinery.
|
||||
* As an optimization we use lighter-weight [RestrictedContinuationImpl] base class (it has less fields) if the context is
|
||||
* [EmptyCoroutineContext], and a full-blown [ContinuationImpl] class otherwise.
|
||||
*
|
||||
* The instance of [BaseContinuationImpl] is passed to the [block] so that it can be passed to the corresponding invocation.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
private inline fun <T> createCoroutineFromSuspendFunction(
|
||||
completion: Continuation<T>,
|
||||
crossinline block: (Continuation<T>) -> Any?
|
||||
): Continuation<Unit> {
|
||||
val context = completion.context
|
||||
// label == 0 when coroutine is not started yet (initially) or label == 1 when it was
|
||||
return if (context === EmptyCoroutineContext)
|
||||
object : RestrictedContinuationImpl(completion as Continuation<Any?>) {
|
||||
private var label = 0
|
||||
|
||||
override fun invokeSuspend(result: Result<Any?>): Any? =
|
||||
when (label) {
|
||||
0 -> {
|
||||
label = 1
|
||||
result.getOrThrow() // Rethrow exception if trying to start with exception (will be caught by BaseContinuationImpl.resumeWith
|
||||
block(this) // run the block, may return or suspend
|
||||
}
|
||||
1 -> {
|
||||
label = 2
|
||||
result.getOrThrow() // this is the result if the block had suspended
|
||||
}
|
||||
else -> error("This coroutine had already completed")
|
||||
}
|
||||
}
|
||||
else
|
||||
object : ContinuationImpl(completion as Continuation<Any?>, context) {
|
||||
private var label = 0
|
||||
|
||||
override fun invokeSuspend(result: Result<Any?>): Any? =
|
||||
when (label) {
|
||||
0 -> {
|
||||
label = 1
|
||||
result.getOrThrow() // Rethrow exception if trying to start with exception (will be caught by BaseContinuationImpl.resumeWith
|
||||
block(this) // run the block, may return or suspend
|
||||
}
|
||||
1 -> {
|
||||
label = 2
|
||||
result.getOrThrow() // this is the result if the block had suspended
|
||||
}
|
||||
else -> error("This coroutine had already completed")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.coroutines.jvm.internal
|
||||
|
||||
import java.io.Serializable
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||
import kotlin.jvm.internal.FunctionBase
|
||||
import kotlin.jvm.internal.Reflection
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
internal abstract class BaseContinuationImpl(
|
||||
// This is `public val` so that it is private on JVM and cannot be modified by untrusted code, yet
|
||||
// it has a public getter (since even untrusted code is allowed to inspect its call stack).
|
||||
public val completion: Continuation<Any?>?
|
||||
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
|
||||
// This implementation is final. This fact is used to unroll resumeWith recursion.
|
||||
public final override fun resumeWith(result: Result<Any?>) {
|
||||
// Invoke "resume" debug probe only once, even if previous frames are "resumed" in the loop below, too
|
||||
probeCoroutineResumed(this)
|
||||
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
|
||||
var current = this
|
||||
var param = result
|
||||
while (true) {
|
||||
with(current) {
|
||||
val completion = completion!! // fail fast when trying to resume continuation without completion
|
||||
val outcome: Result<Any?> =
|
||||
try {
|
||||
val outcome = invokeSuspend(param)
|
||||
if (outcome === COROUTINE_SUSPENDED) return
|
||||
Result.success(outcome)
|
||||
} catch (exception: Throwable) {
|
||||
Result.failure(exception)
|
||||
}
|
||||
releaseIntercepted() // this state machine instance is terminating
|
||||
if (completion is BaseContinuationImpl) {
|
||||
// unrolling recursion via loop
|
||||
current = completion
|
||||
param = outcome
|
||||
} else {
|
||||
// top-level completion reached -- invoke and return
|
||||
completion.resumeWith(outcome)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun invokeSuspend(result: Result<Any?>): Any?
|
||||
|
||||
protected open fun releaseIntercepted() {
|
||||
// does nothing here, overridden in ContinuationImpl
|
||||
}
|
||||
|
||||
public open fun create(completion: Continuation<*>): Continuation<Unit> {
|
||||
throw UnsupportedOperationException("create(Continuation) has not been overridden")
|
||||
}
|
||||
|
||||
public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> {
|
||||
throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden")
|
||||
}
|
||||
|
||||
public override fun toString(): String =
|
||||
"Continuation at ${getStackTraceElement() ?: this::class.java.name}"
|
||||
|
||||
// --- CoroutineStackFrame implementation
|
||||
|
||||
public override val callerFrame: CoroutineStackFrame?
|
||||
get() = completion as? CoroutineStackFrame
|
||||
|
||||
public override fun getStackTraceElement(): StackTraceElement? =
|
||||
getStackTraceElementImpl()
|
||||
}
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
// State machines for named restricted suspend functions extend from this class
|
||||
internal abstract class RestrictedContinuationImpl(
|
||||
completion: Continuation<Any?>?
|
||||
) : BaseContinuationImpl(completion) {
|
||||
init {
|
||||
completion?.let {
|
||||
require(it.context === EmptyCoroutineContext) {
|
||||
"Coroutines with restricted suspension must have EmptyCoroutineContext"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override val context: CoroutineContext
|
||||
get() = EmptyCoroutineContext
|
||||
}
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
// State machines for named suspend functions extend from this class
|
||||
internal abstract class ContinuationImpl(
|
||||
completion: Continuation<Any?>?,
|
||||
private val _context: CoroutineContext?
|
||||
) : BaseContinuationImpl(completion) {
|
||||
constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)
|
||||
|
||||
public override val context: CoroutineContext
|
||||
get() = _context!!
|
||||
|
||||
@Transient
|
||||
private var intercepted: Continuation<Any?>? = null
|
||||
|
||||
public fun intercepted(): Continuation<Any?> =
|
||||
intercepted
|
||||
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
|
||||
.also { intercepted = it }
|
||||
|
||||
protected override fun releaseIntercepted() {
|
||||
val intercepted = intercepted
|
||||
if (intercepted != null && intercepted !== this) {
|
||||
context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
|
||||
}
|
||||
this.intercepted = CompletedContinuation // just in case
|
||||
}
|
||||
}
|
||||
|
||||
internal object CompletedContinuation : Continuation<Any?> {
|
||||
override val context: CoroutineContext
|
||||
get() = error("This continuation is already complete")
|
||||
|
||||
override fun resumeWith(result: Result<Any?>) {
|
||||
error("This continuation is already complete")
|
||||
}
|
||||
|
||||
override fun toString(): String = "This continuation is already complete"
|
||||
}
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
// To distinguish suspend function types from ordinary function types all suspend function types shall implement this interface
|
||||
internal interface SuspendFunction
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
// Restricted suspension lambdas inherit from this class
|
||||
internal abstract class RestrictedSuspendLambda(
|
||||
public override val arity: Int,
|
||||
completion: Continuation<Any?>?
|
||||
) : RestrictedContinuationImpl(completion), FunctionBase<Any?>, SuspendFunction {
|
||||
constructor(arity: Int) : this(arity, null)
|
||||
|
||||
public override fun toString(): String =
|
||||
if (completion == null)
|
||||
Reflection.renderLambdaToString(this) // this is lambda
|
||||
else
|
||||
super.toString() // this is continuation
|
||||
}
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
// Suspension lambdas inherit from this class
|
||||
internal abstract class SuspendLambda(
|
||||
public override val arity: Int,
|
||||
completion: Continuation<Any?>?
|
||||
) : ContinuationImpl(completion), FunctionBase<Any?>, SuspendFunction {
|
||||
constructor(arity: Int) : this(arity, null)
|
||||
|
||||
public override fun toString(): String =
|
||||
if (completion == null)
|
||||
Reflection.renderLambdaToString(this) // this is lambda
|
||||
else
|
||||
super.toString() // this is continuation
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.coroutines.jvm.internal
|
||||
|
||||
import kotlin.coroutines.*
|
||||
|
||||
/**
|
||||
* Represents one frame in the coroutine call stack for debugger.
|
||||
* This interface is implemented by compiler-generated implementations of
|
||||
* [Continuation] interface.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public interface CoroutineStackFrame {
|
||||
/**
|
||||
* Returns a reference to the stack frame of the caller of this frame,
|
||||
* that is a frame before this frame in coroutine call stack.
|
||||
* The result is `null` for the first frame of coroutine.
|
||||
*/
|
||||
public val callerFrame: CoroutineStackFrame?
|
||||
|
||||
/**
|
||||
* Returns stack trace element that correspond to this stack frame.
|
||||
* The result is `null` if the stack trace element is not available for this frame.
|
||||
* In this case, the debugger represents this stack frame using the
|
||||
* result of [toString] function.
|
||||
*/
|
||||
public fun getStackTraceElement(): StackTraceElement?
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.coroutines.jvm.internal
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@SinceKotlin("1.3")
|
||||
internal annotation class DebugMetadata(
|
||||
@get:JvmName("v")
|
||||
val version: Int = 1,
|
||||
@get:JvmName("f")
|
||||
val sourceFile: String = "",
|
||||
@get:JvmName("l")
|
||||
val lineNumbers: IntArray = [],
|
||||
@get:JvmName("n")
|
||||
val localNames: Array<String> = [],
|
||||
@get:JvmName("s")
|
||||
val spilled: Array<String> = [],
|
||||
@get:JvmName("i")
|
||||
val indexToLabel: IntArray = [],
|
||||
@get:JvmName("m")
|
||||
val methodName: String = "",
|
||||
@get:JvmName("c")
|
||||
val className: String = ""
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns [StackTraceElement] containing file name and line number of current coroutine's suspension point.
|
||||
* The coroutine can be either running coroutine, that calls the function on its continuation and obtaining
|
||||
* the information about current file and line number, or, more likely, the function is called to produce accurate stack traces of
|
||||
* suspended coroutine.
|
||||
*
|
||||
* The result is `null` when debug metadata is not available.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@JvmName("getStackTraceElement")
|
||||
internal fun BaseContinuationImpl.getStackTraceElementImpl(): StackTraceElement? {
|
||||
val debugMetadata = getDebugMetadataAnnotation() ?: return null
|
||||
checkDebugMetadataVersion(COROUTINES_DEBUG_METADATA_VERSION, debugMetadata.version)
|
||||
val label = getLabel()
|
||||
val lineNumber = if (label < 0) -1 else debugMetadata.lineNumbers[label]
|
||||
return StackTraceElement(debugMetadata.className, debugMetadata.methodName, debugMetadata.sourceFile, lineNumber)
|
||||
}
|
||||
|
||||
private fun BaseContinuationImpl.getDebugMetadataAnnotation(): DebugMetadata? =
|
||||
javaClass.getAnnotation(DebugMetadata::class.java)
|
||||
|
||||
private fun BaseContinuationImpl.getLabel(): Int =
|
||||
try {
|
||||
val field = javaClass.getDeclaredField("label")
|
||||
field.isAccessible = true
|
||||
(field.get(this) as? Int ?: 0) - 1
|
||||
} catch (e: Exception) { // NoSuchFieldException, SecurityException, or IllegalAccessException
|
||||
-1
|
||||
}
|
||||
|
||||
private fun checkDebugMetadataVersion(expected: Int, actual: Int) {
|
||||
if (actual > expected) {
|
||||
error("Debug metadata version mismatch. Expected: $expected, got $actual. Please update the Kotlin standard library.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of spilled variable names and continuation's field names where the variable has been spilled.
|
||||
* The structure is the following:
|
||||
* - field names take 2*k'th indices
|
||||
* - corresponding variable names take (2*k + 1)'th indices.
|
||||
*
|
||||
* The function is for debugger to use, thus it returns simplest data type possible.
|
||||
* This function should only be called on suspended coroutines to get accurate mapping.
|
||||
*
|
||||
* The result is `null` when debug metadata is not available.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@JvmName("getSpilledVariableFieldMapping")
|
||||
internal fun BaseContinuationImpl.getSpilledVariableFieldMapping(): Array<String>? {
|
||||
val debugMetadata = getDebugMetadataAnnotation() ?: return null
|
||||
checkDebugMetadataVersion(COROUTINES_DEBUG_METADATA_VERSION, debugMetadata.version)
|
||||
val res = arrayListOf<String>()
|
||||
val label = getLabel()
|
||||
for ((i, labelOfIndex) in debugMetadata.indexToLabel.withIndex()) {
|
||||
if (labelOfIndex == label) {
|
||||
res.add(debugMetadata.spilled[i])
|
||||
res.add(debugMetadata.localNames[i])
|
||||
}
|
||||
}
|
||||
return res.toTypedArray()
|
||||
}
|
||||
|
||||
private const val COROUTINES_DEBUG_METADATA_VERSION = 1
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.coroutines.jvm.internal
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.intrinsics.*
|
||||
|
||||
/**
|
||||
* This probe is invoked when coroutine is being created and it can replace completion
|
||||
* with its own wrapped object to intercept completion of this coroutine.
|
||||
*
|
||||
* This probe is invoked from stdlib implementation of [createCoroutineUnintercepted] function.
|
||||
*
|
||||
* Once created, coroutine is repeatedly [resumed][probeCoroutineResumed] and [suspended][probeCoroutineSuspended],
|
||||
* until it is complete. On completion, the object that was returned by this probe is invoked.
|
||||
*
|
||||
* ```
|
||||
* +-------+ probeCoroutineCreated +-----------+
|
||||
* | START | ---------------------->| SUSPENDED |
|
||||
* +-------+ +-----------+
|
||||
* probeCoroutineResumed | ^ probeCoroutineSuspended
|
||||
* V |
|
||||
* +------------+ completion invoked +-----------+
|
||||
* | RUNNING | ------------------->| COMPLETED |
|
||||
* +------------+ +-----------+
|
||||
* ```
|
||||
*
|
||||
* While the coroutine is resumed and suspended, it is represented by the pointer to its `frame`
|
||||
* which always extends [BaseContinuationImpl] and represents a pointer to the topmost frame of the
|
||||
* coroutine. Each [BaseContinuationImpl] object has [completion][BaseContinuationImpl.completion] reference
|
||||
* that points either to another frame (extending [BaseContinuationImpl]) or to the completion object
|
||||
* that was returned by this `probeCoroutineCreated` function.
|
||||
*
|
||||
* When coroutine is [suspended][probeCoroutineSuspended], then it is later [resumed][probeCoroutineResumed]
|
||||
* with a reference to the same frame. However, while coroutine is running it can unwind its frames and
|
||||
* invoke other suspending functions, so its next suspension can happen with a different frame pointer.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
internal fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> {
|
||||
/** implementation of this function is replaced by debugger */
|
||||
return completion
|
||||
}
|
||||
|
||||
/**
|
||||
* This probe is invoked when coroutine is resumed using [Continuation.resumeWith].
|
||||
*
|
||||
* This probe is invoked from stdlib implementation of [BaseContinuationImpl.resumeWith] function.
|
||||
*
|
||||
* Coroutines machinery implementation guarantees that the actual [frame] instance extends
|
||||
* [BaseContinuationImpl] class, despite the fact that the declared type of [frame]
|
||||
* parameter in this function is `Continuation<*>`. See [probeCoroutineCreated] for details.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun probeCoroutineResumed(frame: Continuation<*>) {
|
||||
/** implementation of this function is replaced by debugger */
|
||||
}
|
||||
|
||||
/**
|
||||
* This probe is invoked when coroutine is suspended using [suspendCoroutineUninterceptedOrReturn], that is
|
||||
* when the corresponding `block` returns [COROUTINE_SUSPENDED].
|
||||
*
|
||||
* This probe is invoked from compiler-generated intrinsic for [suspendCoroutineUninterceptedOrReturn] function.
|
||||
*
|
||||
* Coroutines machinery implementation guarantees that the actual [frame] instance extends
|
||||
* [BaseContinuationImpl] class, despite the fact that the declared type of [frame]
|
||||
* parameter in this function is `Continuation<*>`. See [probeCoroutineCreated] for details.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun probeCoroutineSuspended(frame: Continuation<*>) {
|
||||
/** implementation of this function is replaced by debugger */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.coroutines.jvm.internal
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.startCoroutine
|
||||
|
||||
/**
|
||||
* Wrapper for `suspend fun main` and `@Test suspend fun testXXX` functions.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
internal fun runSuspend(block: suspend () -> Unit) {
|
||||
val run = RunSuspend()
|
||||
block.startCoroutine(run)
|
||||
run.await()
|
||||
}
|
||||
|
||||
private class RunSuspend : Continuation<Unit> {
|
||||
override val context: CoroutineContext
|
||||
get() = EmptyCoroutineContext
|
||||
|
||||
var result: Result<Unit>? = null
|
||||
|
||||
override fun resumeWith(result: Result<Unit>) = synchronized(this) {
|
||||
this.result = result
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (this as Object).notifyAll()
|
||||
}
|
||||
|
||||
fun await() = synchronized(this) {
|
||||
while (true) {
|
||||
when (val result = this.result) {
|
||||
null -> @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (this as Object).wait()
|
||||
else -> {
|
||||
result.getOrThrow() // throw up failure
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:JvmName("Boxing")
|
||||
|
||||
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
|
||||
package kotlin.coroutines.jvm.internal
|
||||
|
||||
/*
|
||||
* Box primitive to Java wrapper class by allocating the wrapper object.
|
||||
*
|
||||
* This allows HotSpot JIT to eliminate allocations completely in coroutines code with primitives.
|
||||
*/
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
internal fun boxBoolean(primitive: Boolean): java.lang.Boolean = java.lang.Boolean.valueOf(primitive) as java.lang.Boolean
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
internal fun boxByte(primitive: Byte): java.lang.Byte = java.lang.Byte.valueOf(primitive) as java.lang.Byte
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
internal fun boxShort(primitive: Short): java.lang.Short = java.lang.Short(primitive)
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
internal fun boxInt(primitive: Int): java.lang.Integer = java.lang.Integer(primitive)
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
internal fun boxLong(primitive: Long): java.lang.Long = java.lang.Long(primitive)
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
internal fun boxFloat(primitive: Float): java.lang.Float = java.lang.Float(primitive)
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
internal fun boxDouble(primitive: Double): java.lang.Double = java.lang.Double(primitive)
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
internal fun boxChar(primitive: Char): java.lang.Character = java.lang.Character(primitive)
|
||||
Reference in New Issue
Block a user