[TD] Create real helpers files for coroutines checkers
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import kotlin.coroutines.*
|
||||||
|
import kotlin.coroutines.intrinsics.*
|
||||||
|
import kotlin.coroutines.jvm.internal.*
|
||||||
|
|
||||||
|
fun <T> handleResultContinuation(x: (T) -> Unit): Continuation<T> = object: Continuation<T> {
|
||||||
|
override val context = EmptyCoroutineContext
|
||||||
|
override fun resumeWith(result: Result<T>) {
|
||||||
|
x(result.getOrThrow())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation<Any?> = object: Continuation<Any?> {
|
||||||
|
override val context = EmptyCoroutineContext
|
||||||
|
override fun resumeWith(result: Result<Any?>) {
|
||||||
|
result.exceptionOrNull()?.let(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class EmptyContinuation(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation<Any?> {
|
||||||
|
companion object : EmptyContinuation()
|
||||||
|
override fun resumeWith(result: Result<Any?>) {
|
||||||
|
result.getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResultContinuation : Continuation<Any?> {
|
||||||
|
override val context = EmptyCoroutineContext
|
||||||
|
override fun resumeWith(result: Result<Any?>) {
|
||||||
|
this.result = result.getOrThrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
var result: Any? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ContinuationAdapter<in T> : Continuation<T> {
|
||||||
|
override val context: CoroutineContext = EmptyCoroutineContext
|
||||||
|
override fun resumeWith(result: Result<T>) {
|
||||||
|
if (result.isSuccess) {
|
||||||
|
resume(result.getOrThrow())
|
||||||
|
} else {
|
||||||
|
resumeWithException(result.exceptionOrNull()!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun resumeWithException(exception: Throwable)
|
||||||
|
abstract fun resume(value: T)
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import kotlin.coroutines.*
|
||||||
|
import kotlin.coroutines.intrinsics.*
|
||||||
|
import kotlin.coroutines.jvm.internal.*
|
||||||
|
|
||||||
|
class StateMachineCheckerClass {
|
||||||
|
private var counter = 0
|
||||||
|
var finished = false
|
||||||
|
|
||||||
|
var proceed: () -> Unit = {}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
counter = 0
|
||||||
|
finished = false
|
||||||
|
proceed = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun suspendHere() = suspendCoroutine<Unit> { c ->
|
||||||
|
counter++
|
||||||
|
proceed = { c.resume(Unit) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun check(numberOfSuspensions: Int, checkFinished: Boolean = true) {
|
||||||
|
for (i in 1..numberOfSuspensions) {
|
||||||
|
if (counter != i) error("Wrong state-machine generated: suspendHere should be called exactly once in one state. Expected " + i + ", got " + counter)
|
||||||
|
proceed()
|
||||||
|
}
|
||||||
|
if (counter != numberOfSuspensions)
|
||||||
|
error("Wrong state-machine generated: wrong number of overall suspensions. Expected " + numberOfSuspensions + ", got " + counter)
|
||||||
|
if (finished) error("Wrong state-machine generated: it is finished early")
|
||||||
|
proceed()
|
||||||
|
if (checkFinished && !finished) error("Wrong state-machine generated: it is not finished yet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val StateMachineChecker = StateMachineCheckerClass()
|
||||||
|
|
||||||
|
object CheckStateMachineContinuation: ContinuationAdapter<Unit>() {
|
||||||
|
override val context: CoroutineContext
|
||||||
|
get() = EmptyCoroutineContext
|
||||||
|
|
||||||
|
override fun resume(value: Unit) {
|
||||||
|
StateMachineChecker.proceed = {
|
||||||
|
StateMachineChecker.finished = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import kotlin.coroutines.*
|
||||||
|
import kotlin.coroutines.intrinsics.*
|
||||||
|
import kotlin.coroutines.jvm.internal.*
|
||||||
|
|
||||||
|
class TailCallOptimizationCheckerClass {
|
||||||
|
private val stackTrace = arrayListOf<StackTraceElement?>()
|
||||||
|
|
||||||
|
suspend fun saveStackTrace() = suspendCoroutineUninterceptedOrReturn<Unit> {
|
||||||
|
saveStackTrace(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveStackTrace(c: Continuation<*>) {
|
||||||
|
if (c !is CoroutineStackFrame) error("Continuation " + c + " is not subtype of CoroutineStackFrame")
|
||||||
|
stackTrace.clear()
|
||||||
|
var csf: CoroutineStackFrame? = c
|
||||||
|
while (csf != null) {
|
||||||
|
stackTrace.add(csf.getStackTraceElement())
|
||||||
|
csf = csf.callerFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkNoStateMachineIn(method: String) {
|
||||||
|
stackTrace.find { it?.methodName?.startsWith(method) == true }?.let { error("tail-call optimization miss: method at " + it + " has state-machine " +
|
||||||
|
stackTrace.joinToString(separator = "\n")) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkStateMachineIn(method: String) {
|
||||||
|
stackTrace.find { it?.methodName?.startsWith(method) == true } ?: error("tail-call optimization hit: method " + method + " has no state-machine " +
|
||||||
|
stackTrace.joinToString(separator = "\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val TailCallOptimizationChecker = TailCallOptimizationCheckerClass()
|
||||||
Reference in New Issue
Block a user