~ release intercepted + loop unrolling
diff --git a/libraries/stdlib/coroutines/js/src/kotlin/coroutines/js/internal/ContinuationImpl.kt b/libraries/stdlib/coroutines/js/src/kotlin/coroutines/js/internal/ContinuationImpl.kt
index 2131354..ef19a4b 100644
--- a/libraries/stdlib/coroutines/js/src/kotlin/coroutines/js/internal/ContinuationImpl.kt
+++ b/libraries/stdlib/coroutines/js/src/kotlin/coroutines/js/internal/ContinuationImpl.kt
@@ -5,7 +5,7 @@
package kotlin.coroutines
-import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
+import kotlin.coroutines.intrinsics.CoroutineSingletons
@SinceKotlin("1.3")
@JsName("CoroutineImpl")
@@ -26,21 +26,72 @@
.also { intercepted_ = it }
override fun resumeWith(result: SuccessOrFailure<Any?>) {
+ var current = this
+ var currentResult: Any? = null
+ var currentException: Throwable? = null
if (result.isSuccess) {
- this.result = result.value
+ currentResult = result.value
} else {
- state = exceptionState
- this.exception = result.exception
+ currentException = result.exception
}
- try {
- val resumeResult = doResume()
- if (resumeResult !== COROUTINE_SUSPENDED) {
- resultContinuation.resume(resumeResult)
+
+ // This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
+ while (true) {
+ with(current) {
+ val completion = resultContinuation
+
+ // Set result and exception fields in the current continuation
+ if (currentException == null) {
+ this.result = currentResult
+ } else {
+ state = exceptionState
+ exception = currentException
+ }
+
+ try {
+ val outcome = doResume()
+ if (outcome === CoroutineSingletons.COROUTINE_SUSPENDED) return
+ currentResult = outcome
+ currentException = null
+ } catch (exception: dynamic) { // Catch all exceptions
+ currentResult = null
+ currentException = exception.unsafeCast<Throwable>()
+ }
+
+ releaseIntercepted() // this state machine instance is terminating
+
+ if (completion is CoroutineImpl) {
+ // unrolling recursion via loop
+ current = completion
+ } else {
+ // top-level completion reached -- invoke and return
+ currentException?.let {
+ completion.resumeWithException(it)
+ } ?: completion.resume(currentResult)
+ return
+ }
}
- } catch (t: Throwable) {
- resultContinuation.resumeWithException(t)
}
}
+ private fun releaseIntercepted() {
+ val intercepted = intercepted_
+ if (intercepted != null && intercepted !== this) {
+ context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
+ }
+ this.intercepted_ = CompletedContinuation // just in case
+ }
+
protected abstract fun doResume(): Any?
+}
+
+internal object CompletedContinuation : Continuation<Any?> {
+ override val context: CoroutineContext
+ get() = error("This continuation is already complete")
+
+ override fun resumeWith(result: SuccessOrFailure<Any?>) {
+ error("This continuation is already complete")
+ }
+
+ override fun toString(): String = "This continuation is already complete"
}
\ No newline at end of file