[K/JS] Compile Kotlin coroutines as JS generator ^KT-63038 Fixed

This commit is contained in:
Artem Kobzar
2023-12-18 17:13:07 +00:00
committed by Space Team
parent 4d07fdf97e
commit 2530cba82a
73 changed files with 2240 additions and 144 deletions
@@ -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
}
}