[K/JS] Compile Kotlin coroutines as JS generator ^KT-63038 Fixed
This commit is contained in:
@@ -9,7 +9,9 @@ import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@JsName("CoroutineImpl")
|
||||
internal abstract class CoroutineImpl(private val resultContinuation: Continuation<Any?>?) : Continuation<Any?> {
|
||||
internal abstract class CoroutineImpl(
|
||||
private val resultContinuation: Continuation<Any?>?
|
||||
) : InterceptedCoroutine(), Continuation<Any?> {
|
||||
protected var state = 0
|
||||
protected var exceptionState = 0
|
||||
protected var result: dynamic = null
|
||||
@@ -20,13 +22,6 @@ internal abstract class CoroutineImpl(private val resultContinuation: Continuati
|
||||
|
||||
public override val context: CoroutineContext get() = _context!!
|
||||
|
||||
private var intercepted_: Continuation<Any?>? = null
|
||||
|
||||
public fun intercepted(): Continuation<Any?> =
|
||||
intercepted_
|
||||
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
|
||||
.also { intercepted_ = it }
|
||||
|
||||
override fun resumeWith(result: Result<Any?>) {
|
||||
var current = this
|
||||
var currentResult: Any? = result.getOrNull()
|
||||
@@ -73,14 +68,6 @@ internal abstract class CoroutineImpl(private val resultContinuation: Continuati
|
||||
}
|
||||
}
|
||||
|
||||
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?
|
||||
|
||||
public open fun create(completion: Continuation<*>): Continuation<Unit> {
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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 kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||
import kotlin.internal.InlineOnly
|
||||
|
||||
// It should be replaced with the regular function generator after the bootstrapping
|
||||
internal val dummyGenerator = js("""
|
||||
// TO PREVENT PREVIOUS VERSIONS OF THE COMPILER FAIL TO COMPILE THE CODE
|
||||
var generatorFactory = new Function("return function*(suspended, c) { var a = c(); if (a === suspended) a = yield a; return a }")
|
||||
generatorFactory()
|
||||
""")
|
||||
|
||||
internal val GeneratorFunction = dummyGenerator.constructor.prototype
|
||||
|
||||
internal fun isGeneratorSuspendStep(value: dynamic): Boolean {
|
||||
return value != null && value.constructor === GeneratorFunction
|
||||
}
|
||||
|
||||
internal external interface JsIterationStep<T> {
|
||||
val done: Boolean
|
||||
val value: T
|
||||
}
|
||||
|
||||
internal external interface JsIterator<T> {
|
||||
fun next(value: Any? = definedExternally): JsIterationStep<T>
|
||||
|
||||
@JsName("throw")
|
||||
fun throws(exception: Throwable = definedExternally): JsIterationStep<T>
|
||||
}
|
||||
|
||||
internal class GeneratorCoroutineImpl(val resultContinuation: Continuation<Any?>?) : InterceptedCoroutine(), Continuation<Any?> {
|
||||
private val jsIterators = arrayOf<JsIterator<Any?>>()
|
||||
private val _context = resultContinuation?.context
|
||||
|
||||
var isRunning: Boolean = false
|
||||
private val unknown: Result<Any?> = Result(js("Symbol()"))
|
||||
private var savedResult: Result<Any?> = unknown
|
||||
|
||||
public override val context: CoroutineContext get() = _context!!
|
||||
|
||||
@InlineOnly
|
||||
public inline fun dropLastIterator() {
|
||||
jsIterators.asDynamic().pop()
|
||||
}
|
||||
|
||||
@InlineOnly
|
||||
public inline fun addNewIterator(iterator: JsIterator<Any?>) {
|
||||
jsIterators.asDynamic().push(iterator)
|
||||
}
|
||||
|
||||
@InlineOnly
|
||||
private inline val isCompleted: Boolean get() = jsIterators.size == 0
|
||||
|
||||
@InlineOnly
|
||||
private inline fun getLastIterator(): JsIterator<Any?> = jsIterators[jsIterators.size - 1]
|
||||
|
||||
@InlineOnly
|
||||
public inline fun shouldResumeImmediately(): Boolean = unknown.value !== savedResult.value
|
||||
|
||||
override fun resumeWith(result: Result<Any?>) {
|
||||
if (unknown.value === savedResult.value) savedResult = result
|
||||
if (isRunning) return
|
||||
|
||||
var currentResult: Any? = savedResult.getOrNull()
|
||||
var currentException: Throwable? = savedResult.exceptionOrNull()
|
||||
|
||||
savedResult = unknown
|
||||
|
||||
var current = this
|
||||
|
||||
while (true) {
|
||||
while (!current.isCompleted) {
|
||||
val jsIterator = current.getLastIterator()
|
||||
val exception = currentException.also { currentException = null }
|
||||
|
||||
isRunning = true
|
||||
|
||||
try {
|
||||
val step = when (exception) {
|
||||
null -> jsIterator.next(currentResult)
|
||||
else -> jsIterator.throws(exception)
|
||||
}
|
||||
|
||||
currentResult = step.value
|
||||
currentException = null
|
||||
|
||||
if (step.done) current.dropLastIterator()
|
||||
if (unknown.value !== savedResult.value) {
|
||||
currentResult = savedResult.getOrNull()
|
||||
currentException = savedResult.exceptionOrNull()
|
||||
savedResult = unknown
|
||||
} else if (currentResult === COROUTINE_SUSPENDED) return
|
||||
} catch (e: Throwable) {
|
||||
currentException = e
|
||||
current.dropLastIterator()
|
||||
} finally {
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
releaseIntercepted()
|
||||
|
||||
val completion = resultContinuation!!
|
||||
|
||||
if (completion is GeneratorCoroutineImpl) {
|
||||
current = completion
|
||||
} else {
|
||||
return if (currentException != null) {
|
||||
completion.resumeWithException(currentException!!)
|
||||
} else {
|
||||
completion.resume(currentResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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
|
||||
|
||||
internal abstract class InterceptedCoroutine : Continuation<Any?> {
|
||||
private var _intercepted: Continuation<Any?>? = null
|
||||
|
||||
public fun intercepted(): Continuation<Any?> =
|
||||
_intercepted
|
||||
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
|
||||
.also { _intercepted = it }
|
||||
|
||||
protected fun releaseIntercepted() {
|
||||
val intercepted = _intercepted
|
||||
if (intercepted != null && intercepted !== this) {
|
||||
context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
|
||||
}
|
||||
this._intercepted = CompletedContinuation
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,7 @@
|
||||
|
||||
package kotlin.coroutines.intrinsics
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.ContinuationInterceptor
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.CoroutineImpl
|
||||
import kotlin.internal.InlineOnly
|
||||
|
||||
@@ -169,8 +167,7 @@ public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
|
||||
(this as? CoroutineImpl)?.intercepted() ?: this
|
||||
|
||||
(this as? InterceptedCoroutine)?.intercepted() ?: this
|
||||
|
||||
private inline fun <T> createCoroutineFromSuspendFunction(
|
||||
completion: Continuation<T>,
|
||||
@@ -183,3 +180,107 @@ private inline fun <T> createCoroutineFromSuspendFunction(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@InlineOnly
|
||||
internal inline fun <T> createCoroutineFromGeneratorFunction(
|
||||
completion: Continuation<T>,
|
||||
crossinline generatorFunction: (Continuation<T>) -> dynamic,
|
||||
): Continuation<Any?> {
|
||||
val continuation = GeneratorCoroutineImpl(completion.unsafeCast<Continuation<Any?>>())
|
||||
continuation.addNewIterator(dummyGenerator(COROUTINE_SUSPENDED) { generatorFunction(continuation) })
|
||||
return continuation
|
||||
}
|
||||
|
||||
@InlineOnly
|
||||
internal inline fun <T> startCoroutineFromGeneratorFunction(
|
||||
completion: Continuation<T>,
|
||||
crossinline generatorFunction: (Continuation<T>) -> dynamic,
|
||||
): Any? {
|
||||
val continuation = GeneratorCoroutineImpl(completion.unsafeCast<Continuation<Any?>>())
|
||||
continuation.isRunning = true
|
||||
val result = generatorFunction(continuation)
|
||||
continuation.isRunning = false
|
||||
if (continuation.shouldResumeImmediately()) continuation.resume(result)
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturnGeneratorVersion(
|
||||
completion: Continuation<T>
|
||||
): Any? = startCoroutineFromGeneratorFunction(completion) {
|
||||
val a = asDynamic()
|
||||
if (jsTypeOf(a) === "function") a(it)
|
||||
else invokeSuspendSuperType(it)
|
||||
}
|
||||
|
||||
internal fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturnGeneratorVersion(
|
||||
receiver: R,
|
||||
completion: Continuation<T>
|
||||
): Any? = startCoroutineFromGeneratorFunction(completion) {
|
||||
val a = asDynamic()
|
||||
if (jsTypeOf(a) === "function") a(receiver, it)
|
||||
else invokeSuspendSuperTypeWithReceiver(receiver, it)
|
||||
}
|
||||
|
||||
internal fun <R, P, T> (suspend R.(P) -> T).startCoroutineUninterceptedOrReturnGeneratorVersion(
|
||||
receiver: R,
|
||||
param: P,
|
||||
completion: Continuation<T>
|
||||
): Any? = startCoroutineFromGeneratorFunction(completion) {
|
||||
val a = asDynamic()
|
||||
if (jsTypeOf(a) === "function") a(receiver, param, it)
|
||||
else invokeSuspendSuperTypeWithReceiverAndParam(receiver, param, it)
|
||||
}
|
||||
|
||||
internal fun <T> (suspend () -> T).createCoroutineUninterceptedGeneratorVersion(
|
||||
completion: Continuation<T>
|
||||
): Continuation<Any?> =
|
||||
createCoroutineFromGeneratorFunction(completion) {
|
||||
val a = asDynamic()
|
||||
if (jsTypeOf(a) === "function") a(it)
|
||||
else invokeSuspendSuperType(it)
|
||||
}
|
||||
|
||||
internal fun <R, T> (suspend R.() -> T).createCoroutineUninterceptedGeneratorVersion(
|
||||
receiver: R,
|
||||
completion: Continuation<T>
|
||||
): Continuation<Unit> =
|
||||
createCoroutineFromGeneratorFunction(completion) {
|
||||
val a = asDynamic()
|
||||
if (jsTypeOf(a) === "function") a(receiver, it)
|
||||
else invokeSuspendSuperTypeWithReceiver(receiver, it)
|
||||
}
|
||||
|
||||
|
||||
internal fun <R, T, P> (suspend R.(P) -> T).createCoroutineUninterceptedGeneratorVersion(
|
||||
receiver: R,
|
||||
param: P,
|
||||
completion: Continuation<T>
|
||||
): Continuation<Unit> =
|
||||
createCoroutineFromGeneratorFunction(completion) {
|
||||
val a = asDynamic()
|
||||
if (jsTypeOf(a) === "function") a(receiver, param, it)
|
||||
else invokeSuspendSuperTypeWithReceiverAndParam(receiver, param, it)
|
||||
}
|
||||
|
||||
|
||||
internal fun suspendOrReturn(value: Any?, continuation: Continuation<Any?>): Any? {
|
||||
if (!isGeneratorSuspendStep(value)) return value
|
||||
|
||||
val iterator = value.unsafeCast<JsIterator<Any?>>()
|
||||
|
||||
if (continuation.asDynamic().constructor !== GeneratorCoroutineImpl::class.js) {
|
||||
return iterator.next().value
|
||||
}
|
||||
|
||||
val generatorCoroutineImpl = continuation.unsafeCast<GeneratorCoroutineImpl>()
|
||||
|
||||
generatorCoroutineImpl.addNewIterator(iterator)
|
||||
try {
|
||||
val iteratorStep = iterator.next()
|
||||
if (iteratorStep.done) generatorCoroutineImpl.dropLastIterator()
|
||||
return iteratorStep.value
|
||||
} catch (e: Throwable) {
|
||||
generatorCoroutineImpl.dropLastIterator()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user