diff --git a/compiler/testData/diagnostics/helpers/coroutines/CoroutineHelpers.kt b/compiler/testData/diagnostics/helpers/coroutines/CoroutineHelpers.kt new file mode 100644 index 00000000000..fbba93231cd --- /dev/null +++ b/compiler/testData/diagnostics/helpers/coroutines/CoroutineHelpers.kt @@ -0,0 +1,49 @@ +package helpers + +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* +import kotlin.coroutines.jvm.internal.* + +fun handleResultContinuation(x: (T) -> Unit): Continuation = object: Continuation { + override val context = EmptyCoroutineContext + override fun resumeWith(result: Result) { + x(result.getOrThrow()) + } +} + +fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation = object: Continuation { + override val context = EmptyCoroutineContext + override fun resumeWith(result: Result) { + result.exceptionOrNull()?.let(x) + } +} + +open class EmptyContinuation(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation { + companion object : EmptyContinuation() + override fun resumeWith(result: Result) { + result.getOrThrow() + } +} + +class ResultContinuation : Continuation { + override val context = EmptyCoroutineContext + override fun resumeWith(result: Result) { + this.result = result.getOrThrow() + } + + var result: Any? = null +} + +abstract class ContinuationAdapter : Continuation { + override val context: CoroutineContext = EmptyCoroutineContext + override fun resumeWith(result: Result) { + if (result.isSuccess) { + resume(result.getOrThrow()) + } else { + resumeWithException(result.exceptionOrNull()!!) + } + } + + abstract fun resumeWithException(exception: Throwable) + abstract fun resume(value: T) +} diff --git a/compiler/testData/diagnostics/helpers/coroutines/StateMachineChecker.kt b/compiler/testData/diagnostics/helpers/coroutines/StateMachineChecker.kt new file mode 100644 index 00000000000..69a47f6aac6 --- /dev/null +++ b/compiler/testData/diagnostics/helpers/coroutines/StateMachineChecker.kt @@ -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 { 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() { + override val context: CoroutineContext + get() = EmptyCoroutineContext + + override fun resume(value: Unit) { + StateMachineChecker.proceed = { + StateMachineChecker.finished = true + } + } + + override fun resumeWithException(exception: Throwable) { + throw exception + } +} diff --git a/compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt b/compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt new file mode 100644 index 00000000000..ad89c2c337c --- /dev/null +++ b/compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt @@ -0,0 +1,35 @@ +package helpers + +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* +import kotlin.coroutines.jvm.internal.* + +class TailCallOptimizationCheckerClass { + private val stackTrace = arrayListOf() + + suspend fun saveStackTrace() = suspendCoroutineUninterceptedOrReturn { + 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()