diff --git a/.idea/dictionaries/svyatoslav_kuzmich.xml b/.idea/dictionaries/svyatoslav_kuzmich.xml
index c5c5f0f09d1..e53219b6c9b 100644
--- a/.idea/dictionaries/svyatoslav_kuzmich.xml
+++ b/.idea/dictionaries/svyatoslav_kuzmich.xml
@@ -18,6 +18,7 @@
sqrt
testsuite
uninstantiable
+ unintercepted
unlinkable
vtable
wabt
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt
index 61c4b2778c6..67270e3b369 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt
@@ -94,9 +94,10 @@ private fun buildRoots(modules: Iterable, context: JsIrBackend
rootDeclarations += dceRuntimeDiagnostic.unreachableDeclarationMethod(context).owner
}
- JsMainFunctionDetector.getMainFunctionOrNull(modules.last())?.let { mainFunction ->
+ // TODO: Generate calls to main as IR->IR lowering and reference coroutineEmptyContinuation directly
+ JsMainFunctionDetector(context).getMainFunctionOrNull(modules.last())?.let { mainFunction ->
rootDeclarations += mainFunction
- if (mainFunction.isSuspend) {
+ if (mainFunction.isLoweredSuspendFunction(context)) {
rootDeclarations += context.coroutineEmptyContinuation.owner
}
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsCommonBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsCommonBackendContext.kt
index 8ce110f8881..8801ab9557c 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsCommonBackendContext.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsCommonBackendContext.kt
@@ -5,22 +5,119 @@
package org.jetbrains.kotlin.ir.backend.js
+import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.CommonBackendContext
+import org.jetbrains.kotlin.backend.common.atMostOne
+import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.descriptors.PropertyDescriptor
+import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
+import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver
-import org.jetbrains.kotlin.ir.declarations.*
+import org.jetbrains.kotlin.ir.declarations.IrClass
+import org.jetbrains.kotlin.ir.declarations.IrProperty
+import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
+import org.jetbrains.kotlin.ir.util.SymbolTable
+import org.jetbrains.kotlin.ir.util.getPropertyGetter
+import org.jetbrains.kotlin.ir.util.getPropertySetter
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.resolve.scopes.MemberScope
interface JsCommonBackendContext : CommonBackendContext {
override val mapping: JsMapping
val inlineClassesUtils: InlineClassesUtils
+ val coroutineSymbols: JsCommonCoroutineSymbols
+
+ val catchAllThrowableType: IrType
+ get() = irBuiltIns.throwableType
+
val es6mode: Boolean
get() = false
}
+// TODO: investigate if it could be removed
+internal fun BackendContext.lazy2(fn: () -> T) = lazy { irFactory.stageController.withInitialIr(fn) }
+
+class JsCommonCoroutineSymbols(
+ symbolTable: SymbolTable,
+ module: ModuleDescriptor,
+ val context: JsCommonBackendContext
+) {
+ val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME)
+ val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_PACKAGE_FQNAME)
+
+ val coroutineImpl =
+ symbolTable.referenceClass(findClass(coroutinePackage.memberScope, COROUTINE_IMPL_NAME))
+
+ val coroutineImplLabelPropertyGetter by context.lazy2 { coroutineImpl.getPropertyGetter("state")!!.owner }
+ val coroutineImplLabelPropertySetter by context.lazy2 { coroutineImpl.getPropertySetter("state")!!.owner }
+ val coroutineImplResultSymbolGetter by context.lazy2 { coroutineImpl.getPropertyGetter("result")!!.owner }
+ val coroutineImplResultSymbolSetter by context.lazy2 { coroutineImpl.getPropertySetter("result")!!.owner }
+ val coroutineImplExceptionPropertyGetter by context.lazy2 { coroutineImpl.getPropertyGetter("exception")!!.owner }
+ val coroutineImplExceptionPropertySetter by context.lazy2 { coroutineImpl.getPropertySetter("exception")!!.owner }
+ val coroutineImplExceptionStatePropertyGetter by context.lazy2 { coroutineImpl.getPropertyGetter("exceptionState")!!.owner }
+ val coroutineImplExceptionStatePropertySetter by context.lazy2 { coroutineImpl.getPropertySetter("exceptionState")!!.owner }
+
+ val continuationClass = symbolTable.referenceClass(
+ coroutinePackage.memberScope.getContributedClassifier(
+ CONTINUATION_NAME,
+ NoLookupLocation.FROM_BACKEND
+ ) as ClassDescriptor
+ )
+
+ val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
+ coroutineIntrinsicsPackage.memberScope.getContributedVariables(
+ COROUTINE_SUSPENDED_NAME,
+ NoLookupLocation.FROM_BACKEND
+ ).filterNot { it.isExpect }.single().getter!!
+ )
+
+ val coroutineGetContext: IrSimpleFunctionSymbol
+ get() {
+ val contextGetter =
+ continuationClass.owner.declarations.filterIsInstance()
+ .atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
+ ?: continuationClass.owner.declarations.filterIsInstance()
+ .atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
+ return contextGetter.symbol
+ }
+
+ val coroutineContextProperty: PropertyDescriptor
+ get() {
+ val vars = coroutinePackage.memberScope.getContributedVariables(
+ COROUTINE_CONTEXT_NAME,
+ NoLookupLocation.FROM_BACKEND
+ )
+ return vars.single()
+ }
+
+ companion object {
+ private val INTRINSICS_PACKAGE_NAME = Name.identifier("intrinsics")
+ private val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
+ private val COROUTINE_CONTEXT_NAME = Name.identifier("coroutineContext")
+ private val COROUTINE_IMPL_NAME = Name.identifier("CoroutineImpl")
+ private val CONTINUATION_NAME = Name.identifier("Continuation")
+ private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("")
+ private val CONTINUATION_CONTEXT_PROPERTY_NAME = Name.identifier("context")
+ private val COROUTINE_PACKAGE_FQNAME = FqName.fromSegments(listOf("kotlin", "coroutines"))
+ private val COROUTINE_INTRINSICS_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME.child(INTRINSICS_PACKAGE_NAME)
+ }
+}
+
+internal fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
+ memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
+
+internal fun findFunctions(memberScope: MemberScope, name: Name): List =
+ memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList()
+
interface InlineClassesUtils {
fun isTypeInlined(type: IrType): Boolean
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt
index a0cd7334b8a..d44ede3db36 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt
@@ -144,23 +144,11 @@ class JsIrBackendContext(
companion object {
val KOTLIN_PACKAGE_FQN = FqName.fromSegments(listOf("kotlin"))
- private val INTRINSICS_PACKAGE_NAME = Name.identifier("intrinsics")
- private val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
- private val COROUTINE_CONTEXT_NAME = Name.identifier("coroutineContext")
- private val COROUTINE_IMPL_NAME = Name.identifier("CoroutineImpl")
- private val CONTINUATION_NAME = Name.identifier("Continuation")
// TODO: what is more clear way reference this getter?
- private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("")
-
- private val CONTINUATION_CONTEXT_PROPERTY_NAME = Name.identifier("context")
private val REFLECT_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("reflect"))
private val JS_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("js"))
private val JS_INTERNAL_PACKAGE_FQNAME = JS_PACKAGE_FQNAME.child(Name.identifier("internal"))
- private val COROUTINE_PACKAGE_FQNAME_12 = FqName.fromSegments(listOf("kotlin", "coroutines", "experimental"))
- private val COROUTINE_PACKAGE_FQNAME_13 = FqName.fromSegments(listOf("kotlin", "coroutines"))
- private val COROUTINE_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME_13
- private val COROUTINE_INTRINSICS_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME.child(INTRINSICS_PACKAGE_NAME)
// TODO: due to name clash those weird suffix is required, remove it once `MemberNameGenerator` is implemented
private val COROUTINE_SUSPEND_OR_RETURN_JS_NAME = "suspendCoroutineUninterceptedOrReturnJS"
@@ -171,12 +159,13 @@ class JsIrBackendContext(
private val internalPackage = module.getPackage(JS_PACKAGE_FQNAME)
- private val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME)
- private val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_PACKAGE_FQNAME)
val dynamicType: IrDynamicType = IrDynamicTypeImpl(null, emptyList(), Variance.INVARIANT)
val intrinsics = JsIntrinsics(irBuiltIns, this)
+ override val catchAllThrowableType: IrType
+ get() = dynamicType
+
override val sharedVariablesManager = JsSharedVariablesManager(this)
override val internalPackageFqn = JS_PACKAGE_FQNAME
@@ -193,6 +182,9 @@ class JsIrBackendContext(
fun getOperatorByName(name: Name, type: IrSimpleType) = operatorMap[name]?.get(type.classifier)
+ override val coroutineSymbols =
+ JsCommonCoroutineSymbols(symbolTable, module, this)
+
override val ir = object : Ir(this, irModuleFragment) {
override val symbols = object : Symbols(this@JsIrBackendContext, irBuiltIns, symbolTable) {
override val throwNullPointerException =
@@ -220,14 +212,9 @@ class JsIrBackendContext(
override val stringBuilder
get() = TODO("not implemented")
override val coroutineImpl =
- symbolTable.referenceClass(findClass(coroutinePackage.memberScope, COROUTINE_IMPL_NAME))
+ coroutineSymbols.coroutineImpl
override val coroutineSuspendedGetter =
- symbolTable.referenceSimpleFunction(
- coroutineIntrinsicsPackage.memberScope.getContributedVariables(
- COROUTINE_SUSPENDED_NAME,
- NoLookupLocation.FROM_BACKEND
- ).filterNot { it.isExpect }.single().getter!!
- )
+ coroutineSymbols.coroutineSuspendedGetter
private val _arraysContentEquals = getFunctions(FqName("kotlin.collections.contentEquals")).mapNotNull {
if (it.extensionReceiverParameter != null && it.extensionReceiverParameter!!.type.isNullable())
@@ -241,7 +228,7 @@ class JsIrBackendContext(
override val getContinuation = symbolTable.referenceSimpleFunction(getJsInternalFunction("getContinuation"))
- override val coroutineContextGetter = symbolTable.referenceSimpleFunction(context.coroutineContextProperty.getter!!)
+ override val coroutineContextGetter = symbolTable.referenceSimpleFunction(context.coroutineSymbols.coroutineContextProperty.getter!!)
override val suspendCoroutineUninterceptedOrReturn =
symbolTable.referenceSimpleFunction(getJsInternalFunction(COROUTINE_SUSPEND_OR_RETURN_JS_NAME))
@@ -308,31 +295,10 @@ class JsIrBackendContext(
getIrClass(JS_INTERNAL_PACKAGE_FQNAME.child(Name.identifier("${it.identifier}CompanionObject")))
}
- val coroutineImpl = ir.symbols.coroutineImpl
- val continuationClass = symbolTable.referenceClass(
- coroutinePackage.memberScope.getContributedClassifier(
- CONTINUATION_NAME,
- NoLookupLocation.FROM_BACKEND
- ) as ClassDescriptor
- )
// Top-level functions forced to be loaded
- val coroutineSuspendOrReturn = ir.symbols.suspendCoroutineUninterceptedOrReturn
- val coroutineSuspendGetter = ir.symbols.coroutineSuspendedGetter
- val coroutineGetContext: IrSimpleFunctionSymbol
- get() {
- val contextGetter =
- continuationClass.owner.declarations.filterIsInstance()
- .atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
- ?: continuationClass.owner.declarations.filterIsInstance()
- .atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
- return contextGetter.symbol
- }
-
- val coroutineGetContextJs
- get() = ir.symbols.coroutineGetContext
val coroutineEmptyContinuation = symbolTable.referenceProperty(
getProperty(
@@ -348,14 +314,6 @@ class JsIrBackendContext(
)
)
- val coroutineContextProperty: PropertyDescriptor
- get() {
- val vars = coroutinePackage.memberScope.getContributedVariables(
- COROUTINE_CONTEXT_NAME,
- NoLookupLocation.FROM_BACKEND
- )
- return vars.single()
- }
val newThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("newThrowable"))
val extendThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("extendThrowable"))
@@ -368,15 +326,6 @@ class JsIrBackendContext(
val suiteFun = getFunctions(FqName("kotlin.test.suite")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
val testFun = getFunctions(FqName("kotlin.test.test")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
- val coroutineImplLabelPropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("state")!!.owner }
- val coroutineImplLabelPropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner }
- val coroutineImplResultSymbolGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("result")!!.owner }
- val coroutineImplResultSymbolSetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("result")!!.owner }
- val coroutineImplExceptionPropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("exception")!!.owner }
- val coroutineImplExceptionPropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("exception")!!.owner }
- val coroutineImplExceptionStatePropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("exceptionState")!!.owner }
- val coroutineImplExceptionStatePropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("exceptionState")!!.owner }
-
val primitiveClassProperties by lazy2 {
primitiveClassesObject.owner.declarations.filterIsInstance()
}
@@ -407,12 +356,6 @@ class JsIrBackendContext(
}.toMap()
}
- private fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
- memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
-
- private fun findFunctions(memberScope: MemberScope, name: Name): List =
- memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList()
-
private fun findProperty(memberScope: MemberScope, name: Name): List =
memberScope.getContributedVariables(name, NoLookupLocation.FROM_BACKEND).toList()
@@ -442,7 +385,4 @@ class JsIrBackendContext(
/*TODO*/
print(message)
}
-
- // TODO: investigate if it could be removed
- fun lazy2(fn: () -> T) = lazy { irFactory.stageController.withInitialIr(fn) }
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt
index e6a94d26f33..dcb62961788 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt
@@ -19,8 +19,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.cleanup.CleanupLowering
-import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendArityStoreLowering
-import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
+import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.*
import org.jetbrains.kotlin.ir.backend.js.lower.inline.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -474,6 +473,29 @@ private val suspendFunctionsLoweringPhase = makeBodyLoweringPhase(
description = "Transform suspend functions into CoroutineImpl instance and build state machine"
)
+private val addContinuationToNonLocalSuspendFunctionsLoweringPhase = makeDeclarationTransformerPhase(
+ ::AddContinuationToNonLocalSuspendFunctionsLowering,
+ name = "AddContinuationToNonLocalSuspendFunctionsLowering",
+ description = "Add explicit continuation as last parameter of non-local suspend functions"
+)
+
+private val addContinuationToLocalSuspendFunctionsLoweringPhase = makeBodyLoweringPhase(
+ ::AddContinuationToLocalSuspendFunctionsLowering,
+ name = "AddContinuationToLocalSuspendFunctionsLowering",
+ description = "Add explicit continuation as last parameter of local suspend functions"
+)
+
+
+private val addContinuationToFunctionCallsLoweringPhase = makeBodyLoweringPhase(
+ ::AddContinuationToFunctionCallsLowering,
+ name = "AddContinuationToFunctionCallsLowering",
+ description = "Replace suspend function calls with calls with continuation",
+ prerequisite = setOf(
+ addContinuationToLocalSuspendFunctionsLoweringPhase,
+ addContinuationToNonLocalSuspendFunctionsLoweringPhase,
+ )
+)
+
private val privateMembersLoweringPhase = makeDeclarationTransformerPhase(
::PrivateMembersLowering,
name = "PrivateMembersLowering",
@@ -812,6 +834,10 @@ private val loweringList = listOf(
suspendFunctionsLoweringPhase,
propertyReferenceLoweringPhase,
interopCallableReferenceLoweringPhase,
+ jsSuspendArityStorePhase,
+ addContinuationToNonLocalSuspendFunctionsLoweringPhase,
+ addContinuationToLocalSuspendFunctionsLoweringPhase,
+ addContinuationToFunctionCallsLoweringPhase,
returnableBlockLoweringPhase,
rangeContainsLoweringPhase,
forLoopsLoweringPhase,
@@ -855,7 +881,6 @@ private val loweringList = listOf(
callsLoweringPhase,
cleanupLoweringPhase,
validateIrAfterLowering,
- jsSuspendArityStorePhase
)
// TODO comment? Eliminate ModuleLowering's? Don't filter them here?
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionCallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionCallsLowering.kt
new file mode 100644
index 00000000000..2ccda86fca1
--- /dev/null
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionCallsLowering.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.ir.backend.js.lower.coroutines
+
+import org.jetbrains.kotlin.backend.common.BodyLoweringPass
+import org.jetbrains.kotlin.backend.common.ir.isSuspend
+import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
+import org.jetbrains.kotlin.backend.common.runOnFilePostfix
+import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
+import org.jetbrains.kotlin.ir.builders.irGet
+import org.jetbrains.kotlin.ir.declarations.*
+import org.jetbrains.kotlin.ir.expressions.IrBody
+import org.jetbrains.kotlin.ir.expressions.IrCall
+import org.jetbrains.kotlin.ir.expressions.IrExpression
+import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
+import org.jetbrains.kotlin.ir.util.irCall
+import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
+
+/**
+ * Add continuation to suspend function calls. Requires [AddContinuationToLocalSuspendFunctionsLowering] and
+ * [AddContinuationToNonLocalSuspendFunctionsLowering] to transform function declarations first.
+ *
+ * Additionally materialize continuation for `getContinuation` intrinsic calls.
+ */
+class AddContinuationToFunctionCallsLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
+ override fun lower(irFile: IrFile) {
+ runOnFilePostfix(irFile, withLocalDeclarations = true)
+ }
+
+ override fun lower(irBody: IrBody, container: IrDeclaration) {
+ val continuation: IrValueParameter by lazy {
+ val function = container as IrSimpleFunction
+ if (function.overriddenSymbols
+ .any { it.owner.name.asString() == "doResume" && it.owner.parent == context.coroutineSymbols.coroutineImpl.owner }
+ ) {
+ function.dispatchReceiverParameter!!
+ } else {
+ function.valueParameters.last()
+ }
+ }
+
+ val builder by lazy { context.createIrBuilder(container.symbol) }
+ fun getContinuation() = builder.irGet(continuation)
+
+ irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
+ override fun visitBody(body: IrBody): IrBody {
+ // Nested bodies are covered by separate `lower` invocation
+ return body
+ }
+
+ override fun visitCall(expression: IrCall): IrExpression {
+ expression.transformChildrenVoid()
+
+ if (!expression.isSuspend) {
+ if (expression.symbol == context.ir.symbols.getContinuation)
+ return getContinuation()
+ return expression
+ }
+
+ val oldFun = expression.symbol.owner
+ val newFun: IrSimpleFunction =
+ context.mapping.suspendFunctionsToFunctionWithContinuations[oldFun]
+ ?: error("No mapping for ${oldFun.fqNameWhenAvailable}")
+
+ return irCall(
+ expression,
+ newFun.symbol,
+ newReturnType = newFun.returnType,
+ newSuperQualifierSymbol = expression.superQualifierSymbol
+ ).also {
+ it.putValueArgument(it.valueArgumentsCount - 1, getContinuation())
+ }
+ }
+ })
+ }
+}
+
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionsLowering.kt
new file mode 100644
index 00000000000..c0e54d0c07f
--- /dev/null
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/AddContinuationToFunctionsLowering.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2010-2021 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 org.jetbrains.kotlin.ir.backend.js.lower.coroutines
+
+import org.jetbrains.kotlin.backend.common.*
+import org.jetbrains.kotlin.backend.common.ir.*
+import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
+import org.jetbrains.kotlin.ir.IrStatement
+import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
+import org.jetbrains.kotlin.ir.builders.*
+import org.jetbrains.kotlin.ir.builders.declarations.*
+import org.jetbrains.kotlin.ir.declarations.*
+import org.jetbrains.kotlin.ir.expressions.*
+import org.jetbrains.kotlin.ir.types.IrType
+import org.jetbrains.kotlin.ir.types.isNullable
+import org.jetbrains.kotlin.ir.types.typeWith
+import org.jetbrains.kotlin.ir.util.*
+import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
+
+/**
+ * Replaces suspend functions with regular non-suspend functions with additional
+ * continuation parameter `$cont` of type [kotlin.coroutines.Continuation].
+ *
+ * Replaces return type with `Any?` or `Any` (for non-nullable types) to indicate that suspend
+ * functions can return special values like [kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED]
+ * which might not be a subtype of original return type.
+ */
+class AddContinuationToNonLocalSuspendFunctionsLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
+ override fun transformFlat(declaration: IrDeclaration): List? =
+ if (declaration is IrSimpleFunction && declaration.isSuspend) {
+ listOf(transformSuspendFunction(context, declaration))
+ } else {
+ null
+ }
+}
+
+/**
+ * Similar to [AddContinuationToNonLocalSuspendFunctionsLowering] but processes local functions.
+ * Useful for Kotlin/JS IR backend which keeps local declarations up until code generation.
+ */
+class AddContinuationToLocalSuspendFunctionsLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
+ override fun lower(irBody: IrBody, container: IrDeclaration) {
+ irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
+ override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
+ declaration.transformChildrenVoid()
+ return if (declaration.isSuspend) {
+ transformSuspendFunction(context, declaration)
+ } else {
+ declaration
+ }
+ }
+ })
+ }
+}
+
+
+private fun transformSuspendFunction(context: JsCommonBackendContext, function: IrSimpleFunction): IrSimpleFunction {
+ val newFunctionWithContinuation = function.getOrCreateFunctionWithContinuationStub(context)
+ // Using custom mapping because number of parameters doesn't match
+ val parameterMapping = function.explicitParameters.zip(newFunctionWithContinuation.explicitParameters).toMap()
+ val newBody = function.moveBodyTo(newFunctionWithContinuation, parameterMapping)
+
+ // Since we are changing return type to Any, function can no longer return unit implicitly.
+ if (
+ function.returnType == context.irBuiltIns.unitType &&
+ newBody is IrBlockBody &&
+ newBody.statements.lastOrNull() !is IrReturn
+ ) {
+ // Adding explicit return of Unit.
+ newBody.statements += context.createIrBuilder(newFunctionWithContinuation.symbol).irReturnUnit()
+ }
+
+ newFunctionWithContinuation.body = newBody
+ return newFunctionWithContinuation
+}
+
+
+private fun IrSimpleFunction.getOrCreateFunctionWithContinuationStub(context: JsCommonBackendContext): IrSimpleFunction {
+ return context.mapping.suspendFunctionsToFunctionWithContinuations.getOrPut(this) {
+ createSuspendFunctionStub(context)
+ }
+}
+
+private fun IrSimpleFunction.createSuspendFunctionStub(context: JsCommonBackendContext): IrSimpleFunction {
+ require(this.isSuspend)
+ return factory.buildFun {
+ updateFrom(this@createSuspendFunctionStub)
+ isSuspend = false
+ name = this@createSuspendFunctionStub.name
+ origin = this@createSuspendFunctionStub.origin
+ returnType = loweredSuspendFunctionReturnType(this@createSuspendFunctionStub, context.irBuiltIns)
+ }.also { function ->
+ function.parent = parent
+
+ function.annotations += annotations
+ function.metadata = metadata
+
+ function.copyAttributes(this)
+ function.copyTypeParametersFrom(this)
+ val substitutionMap = makeTypeParameterSubstitutionMap(this, function)
+ function.copyReceiverParametersFrom(this, substitutionMap)
+
+ function.overriddenSymbols += overriddenSymbols.map {
+ it.owner.getOrCreateFunctionWithContinuationStub(context).symbol
+ }
+ function.valueParameters = valueParameters.map { it.copyTo(function) }
+
+ function.addValueParameter(
+ "\$cont",
+ continuationType(context).substitute(substitutionMap),
+ IrDeclarationOrigin.CONTINUATION
+ )
+ }
+}
+
+private fun IrFunction.continuationType(context: JsCommonBackendContext): IrType {
+ return context.coroutineSymbols.continuationClass.typeWith(returnType)
+}
\ No newline at end of file
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/JsSuspendFunctionsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/JsSuspendFunctionsLowering.kt
index c28e902a7a9..0a19e5be368 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/JsSuspendFunctionsLowering.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/JsSuspendFunctionsLowering.kt
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.common.lower.FinallyBlocksLowering
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.backend.common.lower.ReturnableBlockTransformer
+import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.*
@@ -29,16 +30,18 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
-class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunctionsLowering(ctx) {
+class JsSuspendFunctionsLowering(ctx: JsCommonBackendContext) : AbstractSuspendFunctionsLowering(ctx) {
- private val coroutineImplExceptionPropertyGetter = ctx.coroutineImplExceptionPropertyGetter
- private val coroutineImplExceptionPropertySetter = ctx.coroutineImplExceptionPropertySetter
- private val coroutineImplExceptionStatePropertyGetter = ctx.coroutineImplExceptionStatePropertyGetter
- private val coroutineImplExceptionStatePropertySetter = ctx.coroutineImplExceptionStatePropertySetter
- private val coroutineImplLabelPropertySetter = ctx.coroutineImplLabelPropertySetter
- private val coroutineImplLabelPropertyGetter = ctx.coroutineImplLabelPropertyGetter
- private val coroutineImplResultSymbolGetter = ctx.coroutineImplResultSymbolGetter
- private val coroutineImplResultSymbolSetter = ctx.coroutineImplResultSymbolSetter
+ val coroutineSymbols = ctx.coroutineSymbols
+
+ private val coroutineImplExceptionPropertyGetter = coroutineSymbols.coroutineImplExceptionPropertyGetter
+ private val coroutineImplExceptionPropertySetter = coroutineSymbols.coroutineImplExceptionPropertySetter
+ private val coroutineImplExceptionStatePropertyGetter = coroutineSymbols.coroutineImplExceptionStatePropertyGetter
+ private val coroutineImplExceptionStatePropertySetter = coroutineSymbols.coroutineImplExceptionStatePropertySetter
+ private val coroutineImplLabelPropertySetter = coroutineSymbols.coroutineImplLabelPropertySetter
+ private val coroutineImplLabelPropertyGetter = coroutineSymbols.coroutineImplLabelPropertyGetter
+ private val coroutineImplResultSymbolGetter = coroutineSymbols.coroutineImplResultSymbolGetter
+ private val coroutineImplResultSymbolSetter = coroutineSymbols.coroutineImplResultSymbolSetter
private var coroutineId = 0
@@ -53,7 +56,7 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
argumentToPropertiesMap: Map
) {
val returnableBlockTransformer = ReturnableBlockTransformer(context)
- val finallyBlockTransformer = FinallyBlocksLowering(context, context.dynamicType)
+ val finallyBlockTransformer = FinallyBlocksLowering(context, context.catchAllThrowableType)
val simplifiedFunction =
transformingFunction.transform(finallyBlockTransformer, null).transform(returnableBlockTransformer, null) as IrFunction
@@ -117,7 +120,22 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
coroutineImplExceptionStatePropertySetter,
coroutineImplLabelPropertySetter,
thisReceiver,
- suspendResult.symbol
+ getSuspendResultAsType = { type ->
+ JsIrBuilder.buildImplicitCast(
+ JsIrBuilder.buildGetValue(suspendResult.symbol),
+ type
+ )
+ },
+ setSuspendResultValue = { value ->
+ JsIrBuilder.buildSetVariable(
+ suspendResult.symbol,
+ JsIrBuilder.buildImplicitCast(
+ value,
+ context.irBuiltIns.anyNType
+ ),
+ unit
+ )
+ }
)
body.acceptVoid(stateMachineBuilder)
@@ -179,9 +197,9 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
}
}
- stateMachineFunction.transform(LiveLocalsTransformer(localToPropertyMap, { JsIrBuilder.buildGetValue(thisReceiver) }, unit), null)
// TODO find out why some parents are incorrect
stateMachineFunction.body!!.patchDeclarationParents(stateMachineFunction)
+ stateMachineFunction.transform(LiveLocalsTransformer(localToPropertyMap, { JsIrBuilder.buildGetValue(thisReceiver) }, unit), null)
}
private fun assignStateIds(entryState: SuspendState, subject: IrVariableSymbol, switch: IrWhen, rootLoop: IrLoop) {
@@ -235,16 +253,18 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
}
override fun IrBuilderWithScope.generateDelegatedCall(expectedType: IrType, delegatingCall: IrExpression): IrExpression {
- val fromType = (delegatingCall as? IrCall)?.symbol?.owner?.returnType ?: delegatingCall.type
- if (!needUnboxingOrUnit(fromType, expectedType)) return delegatingCall
+ val functionReturnType = (delegatingCall as? IrCall)?.symbol?.owner?.let { function ->
+ loweredSuspendFunctionReturnType(function, context.irBuiltIns)
+ } ?: delegatingCall.type
- val ctx = this@JsSuspendFunctionsLowering.context
- return irComposite(resultType = fromType) {
- val tmp = createTmpVariable(delegatingCall, irType = fromType)
- val coroutineSuspended = irCall(ctx.coroutineSuspendGetter)
+ if (!needUnboxingOrUnit(functionReturnType, expectedType)) return delegatingCall
+
+ return irComposite(resultType = expectedType) {
+ val tmp = createTmpVariable(delegatingCall, irType = functionReturnType)
+ val coroutineSuspended = irCall(coroutineSymbols.coroutineSuspendedGetter)
val condition = irEqeqeq(irGet(tmp), coroutineSuspended)
- +irIfThen(fromType, condition, irReturn(irReinterpretCast(irGet(tmp), expectedType)))
- +irGet(tmp)
+ +irIfThen(context.irBuiltIns.unitType, condition, irReturn(irGet(tmp)))
+ +irImplicitCast(irGet(tmp), expectedType)
}
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt
index b2f2134b30d..482deaf0c15 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt
@@ -77,7 +77,8 @@ class StateMachineBuilder(
private val exStateSymbolSetter: IrSimpleFunction,
private val stateSymbolSetter: IrSimpleFunction,
private val thisSymbol: IrValueParameterSymbol,
- private val suspendResult: IrVariableSymbol
+ private val getSuspendResultAsType: (IrType) -> IrExpression,
+ private val setSuspendResultValue: (IrExpression) -> IrStatement
) : IrElementVisitorVoid {
private val loopMap = mutableMapOf()
@@ -270,7 +271,6 @@ class StateMachineBuilder(
}
private fun implicitCast(value: IrExpression, toType: IrType) = JsIrBuilder.buildImplicitCast(value, toType)
- private fun reinterpretCast(value: IrExpression, toType: IrType) = JsIrBuilder.buildReinterpretCast(value, toType)
override fun visitCall(expression: IrCall) {
super.visitCall(expression)
@@ -295,11 +295,11 @@ class StateMachineBuilder(
}
}
- addStatement(JsIrBuilder.buildSetVariable(suspendResult, reinterpretCast(result, anyN), unit))
+ addStatement(setSuspendResultValue(result))
- val irReturn = JsIrBuilder.buildReturn(function, JsIrBuilder.buildGetValue(suspendResult), nothing)
+ val irReturn = JsIrBuilder.buildReturn(function, getSuspendResultAsType(anyN), nothing)
val check = JsIrBuilder.buildCall(eqeqeqSymbol).apply {
- putValueArgument(0, JsIrBuilder.buildGetValue(suspendResult))
+ putValueArgument(0, getSuspendResultAsType(anyN))
putValueArgument(1, JsIrBuilder.buildCall(context.ir.symbols.coroutineSuspendedGetter))
}
@@ -318,18 +318,17 @@ class StateMachineBuilder(
unboxState?.let { buildUnboxingState(it, continueState, expectedType) }
updateState(continueState)
- val functionReturnType = expression.symbol.owner.returnType
- addStatement(reinterpretCast(JsIrBuilder.buildGetValue(suspendResult), functionReturnType))
+ addStatement(getSuspendResultAsType(expression.type))
}
}
private fun buildUnboxingState(unboxState: SuspendState, continueState: SuspendState, expectedType: IrType) {
unboxState.successors += continueState
updateState(unboxState)
- val result = JsIrBuilder.buildGetValue(suspendResult)
+ val result = getSuspendResultAsType(anyN)
val tmp = JsIrBuilder.buildVar(expectedType, function.owner, name = "unboxed", initializer = result)
addStatement(tmp)
- addStatement(JsIrBuilder.buildSetVariable(suspendResult, reinterpretCast(JsIrBuilder.buildGetValue(tmp.symbol), anyN), anyN))
+ addStatement(setSuspendResultValue(JsIrBuilder.buildGetValue(tmp.symbol)))
doDispatch(continueState)
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendLoweringUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendLoweringUtils.kt
index 10fe01f50d9..c3c0f43c258 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendLoweringUtils.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendLoweringUtils.kt
@@ -6,9 +6,12 @@
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
import org.jetbrains.kotlin.backend.common.ir.isSuspend
+import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
+import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver
+import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
@@ -17,7 +20,7 @@ import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrType
-import org.jetbrains.kotlin.ir.types.isUnit
+import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.visitors.*
open class SuspendableNodesCollector(private val suspendableNodes: MutableSet) : IrElementVisitorVoid {
@@ -82,7 +85,12 @@ class LiveLocalsTransformer(
) :
IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
- val field = localMap[expression.symbol] ?: return expression
+ val field = localMap[expression.symbol]
+ ?: return if (expression.symbol.owner.isDispatchReceiver)
+ receiver()
+ else
+ expression
+
return expression.run { IrGetFieldImpl(startOffset, endOffset, field, type, receiver(), origin) }
}
@@ -99,7 +107,10 @@ class LiveLocalsTransformer(
return if (initializer != null) {
declaration.run { IrSetFieldImpl(startOffset, endOffset, field, receiver(), initializer, unitType) }
} else {
- JsIrBuilder.buildComposite(declaration.type)
+ JsIrBuilder.buildComposite(unitType)
}
}
-}
\ No newline at end of file
+}
+
+fun loweredSuspendFunctionReturnType(function: IrFunction, irBuiltIns: IrBuiltIns): IrType =
+ if (function.returnType.isNullable()) irBuiltIns.anyNType else irBuiltIns.anyType
\ No newline at end of file
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt
index 69e49c44715..1fe77739ed2 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt
@@ -349,12 +349,16 @@ class IrModuleToJsTransformer(
return statements
}
- private fun generateMainArguments(mainFunction: IrSimpleFunction, rootContext: JsGenerationContext): List {
+ private fun generateMainArguments(
+ generateArgv: Boolean,
+ generateContinuation: Boolean,
+ rootContext: JsGenerationContext
+ ): List {
val mainArguments = this.mainArguments!!
val mainArgumentsArray =
- if (mainFunction.valueParameters.isNotEmpty()) JsArrayLiteral(mainArguments.map { JsStringLiteral(it) }) else null
+ if (generateArgv) JsArrayLiteral(mainArguments.map { JsStringLiteral(it) }) else null
- val continuation = if (mainFunction.isSuspend) {
+ val continuation = if (generateContinuation) {
backendContext.coroutineEmptyContinuation.owner
.let { it.getter!! }
.let { rootContext.getNameForStaticFunction(it) }
@@ -365,11 +369,14 @@ class IrModuleToJsTransformer(
}
private fun generateCallToMain(modules: Iterable, rootContext: JsGenerationContext): List {
+ // TODO: Generate calls to main as IR->IR lowering
if (mainArguments == null) return emptyList() // in case `NO_MAIN` and `main(..)` exists
- val mainFunction = JsMainFunctionDetector.getMainFunctionOrNull(modules.last())
+ val mainFunction = JsMainFunctionDetector(backendContext).getMainFunctionOrNull(modules.last())
return mainFunction?.let {
val jsName = rootContext.getNameForStaticFunction(it)
- listOf(JsInvocation(jsName.makeRef(), generateMainArguments(it, rootContext)).makeStmt())
+ val generateArgv = it.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
+ val generateContinuation = it.isLoweredSuspendFunction(backendContext)
+ listOf(JsInvocation(jsName.makeRef(), generateMainArguments(generateArgv, generateContinuation, rootContext)).makeStmt())
} ?: emptyList()
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt
index cd25b8e2367..abdf528920f 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt
@@ -121,21 +121,9 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
addIfNotNull(intrinsics.jsCode) { _, _ -> error("Should not be called") }
- add(intrinsics.jsGetContinuation) { _, context: JsGenerationContext ->
- context.continuation
- }
+ add(intrinsics.jsGetContinuation) { _, _ -> error("getContinuation should be lowered") }
- add(backendContext.ir.symbols.returnIfSuspended) { call, context ->
- val args = translateCallArguments(call, context)
- args[0]
- }
-
- add(intrinsics.jsCoroutineContext) { _, context: JsGenerationContext ->
- val contextGetter = backendContext.coroutineGetContext
- val getterName = context.getNameForStaticFunction(contextGetter.owner)
- val continuation = context.continuation
- JsInvocation(JsNameRef(getterName, continuation))
- }
+ add(intrinsics.jsCoroutineContext) { _, _ -> error("coroutineContext should be lowered") }
add(intrinsics.jsArrayLength) { call, context ->
val args = translateCallArguments(call, context)
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt
index 4be7a82fc87..c186a5cd3bf 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt
@@ -76,9 +76,7 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForValueDeclaration(it)) }
functionParams.forEach { function.addParameter(it) }
- if (declaration.isSuspend) {
- function.addParameter(JsName(Namer.CONTINUATION)) // TODO: Use namer?
- }
+ check(!declaration.isSuspend) { "All Suspend functions should be lowered" }
return function
}
@@ -336,9 +334,8 @@ fun translateCallArguments(
.dropLastWhile { it == null }
.map { it ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) }
- return if (expression.symbol.isSuspend) {
- arguments + context.continuation
- } else arguments
+ check(!expression.symbol.isSuspend) { "Suspend functions should be lowered" }
+ return arguments
}
private fun IrMemberAccessExpression<*>.validWithNullArgs() =
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt
index 8fce5fc8199..ce7f68c131c 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt
@@ -49,17 +49,6 @@ class JsGenerationContext(
)
}
- val continuation
- get() = if (isCoroutineDoResume()) {
- JsThisRef()
- } else {
- if (currentFunction!!.isSuspend) {
- JsNameRef(Namer.CONTINUATION)
- } else {
- JsNameRef(this.getNameForValueDeclaration(currentFunction.valueParameters.last()))
- }
- }
-
fun getNameForValueDeclaration(declaration: IrDeclarationWithName): JsName {
val name = localNames!!.variableNames.names[declaration]
?: error("Variable name is not found ${declaration.name}")
@@ -76,12 +65,5 @@ class JsGenerationContext(
return JsName(name)
}
- private fun isCoroutineDoResume(): Boolean {
- val overriddenSymbols = (currentFunction as? IrSimpleFunction)?.overriddenSymbols ?: return false
- return overriddenSymbols.any {
- it.owner.name.asString() == "doResume" && it.owner.parent == staticContext.coroutineImplDeclaration
- }
- }
-
fun checkIfJsCode(symbol: IrFunctionSymbol): Boolean = symbol == staticContext.backendContext.intrinsics.jsCode
}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsMainFunctionDetector.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsMainFunctionDetector.kt
index 9e3fec31d63..a53b7d369ce 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsMainFunctionDetector.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsMainFunctionDetector.kt
@@ -5,18 +5,16 @@
package org.jetbrains.kotlin.ir.backend.js.utils
-import org.jetbrains.kotlin.ir.declarations.IrFile
-import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
-import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
+import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.types.Variance
-object JsMainFunctionDetector {
-
+class JsMainFunctionDetector(val context: JsCommonBackendContext) {
private fun IrSimpleFunction.isSuitableForMainParametersSize(allowEmptyParameters: Boolean): Boolean =
when (valueParameters.size) {
- 1 -> true
+ 1, 2 -> true
0 -> allowEmptyParameters
else -> false
}
@@ -24,24 +22,20 @@ object JsMainFunctionDetector {
private fun IrSimpleFunction.isMain(allowEmptyParameters: Boolean): Boolean {
if (typeParameters.isNotEmpty()) return false
if (!isSuitableForMainParametersSize(allowEmptyParameters)) return false
- if (!returnType.isUnit()) return false
+ val isLoweredSuspendFunction = isLoweredSuspendFunction(context)
+ if (!returnType.isUnit() &&
+ !(isLoweredSuspendFunction &&
+ (returnType == context.irBuiltIns.anyNType ||
+ returnType == context.irBuiltIns.anyType)))
+ return false
+
if (name.asString() != "main") return false
if (extensionReceiverParameter != null) return false
if (valueParameters.size == 1) {
- val parameter = valueParameters.single()
-
- if (!parameter.type.isArray()) return false
-
- val type = parameter.type as IrSimpleType
-
- if (type.arguments.size != 1) return false
-
- val argument = type.arguments.single() as? IrTypeProjection ?: return false
-
- if (argument.variance == Variance.IN_VARIANCE) return false
-
- return argument.type.isString()
+ return isLoweredSuspendFunction || valueParameters.single().isStringArrayParameter()
+ } else if (valueParameters.size == 2) {
+ return valueParameters[0].isStringArrayParameter() && isLoweredSuspendFunction
} else {
require(allowEmptyParameters)
require(valueParameters.isEmpty())
@@ -73,4 +67,30 @@ object JsMainFunctionDetector {
return resultPair?.second
}
-}
\ No newline at end of file
+}
+
+
+fun IrValueParameter.isStringArrayParameter(): Boolean {
+ val type = this.type as? IrSimpleType ?: return false
+
+ if (!type.isArray()) return false
+
+ if (type.arguments.size != 1) return false
+
+ val argument = type.arguments.single() as? IrTypeProjection ?: return false
+
+ if (argument.variance == Variance.IN_VARIANCE) return false
+
+ return argument.type.isString()
+}
+
+fun IrFunction.isLoweredSuspendFunction(context: JsCommonBackendContext): Boolean {
+ val parameter = valueParameters.lastOrNull() ?: return false
+ val type = parameter.type as? IrSimpleType ?: return false
+ return type.classifier == context.coroutineSymbols.continuationClass
+}
+
+fun IrValueParameter.isContinuationParameter(context: JsCommonBackendContext): Boolean {
+ val type = this.type as? IrSimpleType ?: return false
+ return type.classifier == context.coroutineSymbols.continuationClass
+}
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt
index 576287bfd48..96d9317b739 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt
@@ -23,8 +23,6 @@ object Namer {
val PROTOTYPE_NAME = "prototype"
val CONSTRUCTOR_NAME = "constructor"
- val CONTINUATION = "\$cont"
-
val JS_ERROR = JsNameRef("Error")
val JS_OBJECT = JsNameRef("Object")
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt
index d02e4941a5e..155b5194f60 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
+import org.jetbrains.kotlin.ir.backend.js.JsCommonCoroutineSymbols
import org.jetbrains.kotlin.ir.backend.js.JsMapping
import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
@@ -57,6 +58,9 @@ class WasmBackendContext(
override val mapping = JsMapping(irFactory)
+ override val coroutineSymbols =
+ JsCommonCoroutineSymbols(symbolTable, module,this)
+
val innerClassesSupport = JsInnerClassesSupport(mapping, irFactory)
override val internalPackageFqn = FqName("kotlin.wasm")
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
index 7bf0212e2de..7c795afdc2d 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
@@ -13,6 +13,9 @@ import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorI
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.wasm.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.*
+import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.AddContinuationToFunctionCallsLowering
+import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
+import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineDeclarationsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.WrapInlineDeclarationsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -80,14 +83,6 @@ private val lateinitUsageLoweringPhase = makeWasmModulePhase(
description = "Insert checks for lateinit field references"
)
-
-// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase
-private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
- { ProvisionalFunctionExpressionLowering() },
- name = "FunctionExpression",
- description = "Transform IrFunctionExpression to a local function reference"
-)
-
private val wrapInlineDeclarationsWithReifiedTypeParametersPhase = makeWasmModulePhase(
::WrapInlineDeclarationsWithReifiedTypeParametersLowering,
name = "WrapInlineDeclarationsWithReifiedTypeParametersPhase",
@@ -247,6 +242,27 @@ private val innerClassConstructorCallsLoweringPhase = makeWasmModulePhase(
description = "Replace inner class constructor invocation"
)
+private val suspendFunctionsLoweringPhase = makeWasmModulePhase(
+ ::JsSuspendFunctionsLowering,
+ name = "SuspendFunctionsLowering",
+ description = "Transform suspend functions into CoroutineImpl instance and build state machine"
+)
+
+private val addContinuationToNonLocalSuspendFunctionsLoweringPhase = makeWasmModulePhase(
+ ::AddContinuationToNonLocalSuspendFunctionsLowering,
+ name = "AddContinuationToNonLocalSuspendFunctionsLowering",
+ description = "Add explicit continuation as last parameter of suspend functions"
+)
+
+private val addContinuationToFunctionCallsLoweringPhase = makeWasmModulePhase(
+ ::AddContinuationToFunctionCallsLowering,
+ name = "AddContinuationToFunctionCallsLowering",
+ description = "Replace suspend function calls with calls with continuation",
+ prerequisite = setOf(
+ addContinuationToNonLocalSuspendFunctionsLoweringPhase,
+ )
+)
+
private val defaultArgumentStubGeneratorPhase = makeWasmModulePhase(
::DefaultArgumentStubGenerator,
name = "DefaultArgumentStubGenerator",
@@ -461,7 +477,6 @@ val wasmPhases = NamedCompilerPhase(
wrapInlineDeclarationsWithReifiedTypeParametersPhase then
functionInliningPhase then
- provisionalFunctionExpressionPhase then
lateinitNullableFieldsPhase then
lateinitDeclarationLoweringPhase then
lateinitUsageLoweringPhase then
@@ -483,8 +498,6 @@ val wasmPhases = NamedCompilerPhase(
propertiesLoweringPhase then
primaryConstructorLoweringPhase then
delegateToPrimaryConstructorLoweringPhase then
- initializersLoweringPhase then
- initializersCleanupLoweringPhase then
// Common prefix ends
enumEntryInstancesLoweringPhase then
@@ -495,8 +508,12 @@ val wasmPhases = NamedCompilerPhase(
enumUsageLoweringPhase then
enumEntryRemovalLoweringPhase then
-// TODO: Requires stdlib
-// suspendFunctionsLoweringPhase then
+ suspendFunctionsLoweringPhase then
+ initializersLoweringPhase then
+ initializersCleanupLoweringPhase then
+
+ addContinuationToNonLocalSuspendFunctionsLoweringPhase then
+ addContinuationToFunctionCallsLoweringPhase then
stringConstructorLowering then
tryCatchCanonicalization then
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
index 4f7c6727bd9..583ffdb9673 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
@@ -55,24 +55,20 @@ class WasmSymbols(
get() = TODO()
override val stringBuilder =
getIrClass(FqName("kotlin.text.StringBuilder"))
- override val coroutineImpl
- get() = TODO()
- override val coroutineSuspendedGetter
- get() = TODO()
- override val getContinuation
- get() = TODO()
- override val coroutineContextGetter by lazy {
- context.irFactory.addFunction(context.getExcludedPackageFragment(FqName("kotlin.excluded"))) {
- name = Name.identifier("coroutineContextGetter\$Stub")
- }.symbol
- }
-
- override val suspendCoroutineUninterceptedOrReturn
- get() = TODO()
- override val coroutineGetContext
- get() = TODO()
- override val returnIfSuspended
- get() = TODO()
+ override val coroutineImpl =
+ context.coroutineSymbols.coroutineImpl
+ override val coroutineSuspendedGetter =
+ context.coroutineSymbols.coroutineSuspendedGetter
+ override val getContinuation =
+ getInternalFunction("getContinuation")
+ override val coroutineContextGetter =
+ symbolTable.referenceSimpleFunction(context.coroutineSymbols.coroutineContextProperty.getter!!)
+ override val suspendCoroutineUninterceptedOrReturn =
+ getInternalFunction("suspendCoroutineUninterceptedOrReturn")
+ override val coroutineGetContext =
+ getInternalFunction("getCoroutineContext")
+ override val returnIfSuspended =
+ getInternalFunction("returnIfSuspended")
override val functionAdapter: IrClassSymbol
get() = TODO()
@@ -153,6 +149,9 @@ class WasmSymbols(
val exportString = getInternalFunction("exportString")
val unsafeGetScratchRawMemory = getInternalFunction("unsafeGetScratchRawMemory")
+ val startCoroutineUninterceptedOrReturnIntrinsics =
+ (0..2).map { getInternalFunction("startCoroutineUninterceptedOrReturnIntrinsic$it") }
+
// KProperty implementations
val kLocalDelegatedPropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedPropertyImpl")
val kLocalDelegatedMutablePropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedMutablePropertyImpl")
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt
index a8c5c4a99fe..77b7d14a5a7 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt
@@ -21,10 +21,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isNullable
-import org.jetbrains.kotlin.ir.util.functions
-import org.jetbrains.kotlin.ir.util.irCall
-import org.jetbrains.kotlin.ir.util.isFunction
-import org.jetbrains.kotlin.ir.util.isNullConst
+import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -121,6 +118,11 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
putValueArgument(0, call.getValueArgument(0))
}
}
+ in symbols.startCoroutineUninterceptedOrReturnIntrinsics -> {
+ val arity = symbols.startCoroutineUninterceptedOrReturnIntrinsics.indexOf(symbol)
+ val newSymbol = irBuiltins.suspendFunctionN(arity).getSimpleFunction("invoke")!!
+ return irCall(call, newSymbol, argumentsAsReceivers = true)
+ }
}
return call
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt
index c9c50a86a39..29f221a1a24 100644
--- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt
+++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt
@@ -78,6 +78,7 @@ interface IrDeclarationOrigin {
object SYNTHETIC_JAVA_PROPERTY_DELEGATE : IrDeclarationOriginImpl("SYNTHETIC_JAVA_PROPERTY_DELEGATE", isSynthetic = true)
object FIELD_FOR_OUTER_THIS : IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS", isSynthetic = true)
+ object CONTINUATION : IrDeclarationOriginImpl("CONTINUATION", isSynthetic = true)
val isSynthetic: Boolean get() = false
}
diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt
index 267d28dfeb9..a93360987b1 100644
--- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt
+++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt
@@ -36,6 +36,9 @@ class JsMapping(val state: JsMappingState) : DefaultMapping(state) {
val enumEntryToCorrespondingField = state.newDeclarationToDeclarationMapping()
val enumClassToInitEntryInstancesFun = state.newDeclarationToDeclarationMapping()
+ val suspendFunctionsToFunctionWithContinuations =
+ state.newDeclarationToDeclarationMapping()
+
val suspendArityStore = state.newDeclarationToDeclarationCollectionMapping>()
}
diff --git a/libraries/stdlib/js-ir/runtime/coroutineInternalJS.kt b/libraries/stdlib/js-ir/runtime/coroutineInternalJS.kt
index 8caddd9ca29..4a2a22e3237 100644
--- a/libraries/stdlib/js-ir/runtime/coroutineInternalJS.kt
+++ b/libraries/stdlib/js-ir/runtime/coroutineInternalJS.kt
@@ -14,8 +14,9 @@ internal fun getContinuation(): Continuation { throw Exception("Implement
// Do we really need this intrinsic in JS?
@PublishedApi
+@Suppress("UNCHECKED_CAST")
internal suspend fun returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T {
- throw Exception("Implemented as intrinsic")
+ return argument as T
}
@PublishedApi
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Coroutines.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Coroutines.kt
new file mode 100644
index 00000000000..53ca8fcfd74
--- /dev/null
+++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Coroutines.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010-2021 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.
+ */
+
+@file:Suppress("RedundantSuspendModifier")
+
+package kotlin.wasm.internal
+
+import kotlin.coroutines.*
+
+@PublishedApi
+@ExcludedFromCodegen
+internal fun getContinuation(): Continuation =
+ implementedAsIntrinsic
+
+@PublishedApi
+@Suppress("UNCHECKED_CAST")
+internal suspend fun returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T =
+ argument as T
+
+@PublishedApi
+internal fun interceptContinuationIfNeeded(
+ context: CoroutineContext,
+ continuation: Continuation
+) = context[ContinuationInterceptor]?.interceptContinuation(continuation) ?: continuation
+
+
+@PublishedApi
+internal suspend fun getCoroutineContext(): CoroutineContext = getContinuation().context
+
+@PublishedApi
+internal suspend fun suspendCoroutineUninterceptedOrReturn(block: (Continuation) -> Any?): T =
+ returnIfSuspended(block(getContinuation()))
+
+@ExcludedFromCodegen
+@PublishedApi
+internal fun startCoroutineUninterceptedOrReturnIntrinsic0(
+ f: (suspend () -> T),
+ completion: Continuation
+): Any? {
+ implementedAsIntrinsic
+}
+
+@ExcludedFromCodegen
+@PublishedApi
+internal fun startCoroutineUninterceptedOrReturnIntrinsic1(
+ f: (suspend R.() -> T),
+ receiver: R,
+ completion: Continuation
+): Any? {
+ implementedAsIntrinsic
+}
+
+@ExcludedFromCodegen
+@PublishedApi
+internal fun startCoroutineUninterceptedOrReturnIntrinsic2(
+ f: (suspend R.(P) -> T),
+ receiver: R,
+ param: P,
+ completion: Continuation
+): Any? {
+ implementedAsIntrinsic
+}
diff --git a/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutineImpl.kt b/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutineImpl.kt
new file mode 100644
index 00000000000..1a695365714
--- /dev/null
+++ b/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutineImpl.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2021 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
+
+@SinceKotlin("1.3")
+internal abstract class CoroutineImpl(private val resultContinuation: Continuation?) : Continuation {
+ protected var state = 0
+ protected var exceptionState = 0
+ protected var result: Any? = null
+ protected var exception: Throwable? = null
+ protected var finallyPath: Array? = null
+
+ private val _context: CoroutineContext? = resultContinuation?.context
+
+ public override val context: CoroutineContext get() = _context!!
+
+ private var intercepted_: Continuation? = null
+
+ public fun intercepted(): Continuation =
+ intercepted_
+ ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
+ .also { intercepted_ = it }
+
+ override fun resumeWith(result: Result) {
+ var current = this
+ var currentResult: Any? = result.getOrNull()
+ var currentException: Throwable? = result.exceptionOrNull()
+
+ // This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
+ while (true) {
+ with(current) {
+ // 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 === COROUTINE_SUSPENDED) return
+ currentResult = outcome
+ currentException = null
+ } catch (exception: Throwable) { // Catch all exceptions
+ currentResult = null
+ currentException = exception
+ }
+
+ releaseIntercepted() // this state machine instance is terminating
+
+ val completion = resultContinuation!!
+
+ if (completion is CoroutineImpl) {
+ // unrolling recursion via loop
+ current = completion
+ } else {
+ // top-level completion reached -- invoke and return
+ if (currentException != null) {
+ completion.resumeWithException(currentException!!)
+ } else {
+ completion.resume(currentResult)
+ }
+ return
+ }
+ }
+ }
+ }
+
+ 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 {
+ throw UnsupportedOperationException("create(Continuation) has not been overridden")
+ }
+
+ public open fun create(value: Any?, completion: Continuation<*>): Continuation {
+ throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden")
+ }
+}
+
+internal object CompletedContinuation : Continuation {
+ override val context: CoroutineContext
+ get() = error("This continuation is already complete")
+
+ override fun resumeWith(result: Result) {
+ error("This continuation is already complete")
+ }
+
+ override fun toString(): String = "This continuation is already complete"
+}
diff --git a/libraries/stdlib/wasm/src/kotlin/coroutines/Coroutines.kt b/libraries/stdlib/wasm/src/kotlin/coroutines/Coroutines.kt
index 0961934ca18..cd91bb6c88d 100644
--- a/libraries/stdlib/wasm/src/kotlin/coroutines/Coroutines.kt
+++ b/libraries/stdlib/wasm/src/kotlin/coroutines/Coroutines.kt
@@ -1,21 +1,53 @@
/*
- * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Copyright 2010-2021 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.CoroutineSingletons.*
+import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
+
@PublishedApi
@SinceKotlin("1.3")
-internal actual class SafeContinuation : Continuation {
- actual internal constructor(delegate: Continuation, initialResult: Any?) { TODO("Wasm stdlib: Coroutines") }
+internal actual class SafeContinuation
+internal actual constructor(
+ private val delegate: Continuation,
+ initialResult: Any?
+) : Continuation {
+ @PublishedApi
+ internal actual constructor(delegate: Continuation) : this(delegate, UNDECIDED)
+
+ public actual override val context: CoroutineContext
+ get() = delegate.context
+
+ private var result: Any? = initialResult
+
+ public actual override fun resumeWith(result: Result) {
+ val cur = this.result
+ when {
+ cur === UNDECIDED -> {
+ this.result = result.value
+ }
+ cur === COROUTINE_SUSPENDED -> {
+ this.result = RESUMED
+ delegate.resumeWith(result)
+ }
+ else -> throw IllegalStateException("Already resumed")
+ }
+ }
@PublishedApi
- actual internal constructor(delegate: Continuation) { TODO("Wasm stdlib: Coroutines") }
-
- @PublishedApi
- actual internal fun getOrThrow(): Any? = TODO("Wasm stdlib: Coroutines")
-
- actual override val context: CoroutineContext = TODO("Wasm stdlib: Coroutines")
- actual override fun resumeWith(result: Result): Unit { TODO("Wasm stdlib: Coroutines") }
-}
+ internal actual fun getOrThrow(): Any? {
+ if (result === UNDECIDED) {
+ result = COROUTINE_SUSPENDED
+ return COROUTINE_SUSPENDED
+ }
+ val result = this.result
+ return when {
+ result === RESUMED -> COROUTINE_SUSPENDED // already called continuation, indicate COROUTINE_SUSPENDED upstream
+ result is Result.Failure -> throw result.exception
+ else -> result // either COROUTINE_SUSPENDED or data
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutinesIntrinsics.kt b/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutinesIntrinsics.kt
index d8959b4a719..c6668bec2de 100644
--- a/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutinesIntrinsics.kt
+++ b/libraries/stdlib/wasm/src/kotlin/coroutines/CoroutinesIntrinsics.kt
@@ -1,14 +1,12 @@
/*
- * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Copyright 2010-2021 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.intrinsics
-import kotlin.coroutines.Continuation
-import kotlin.coroutines.ContinuationInterceptor
-import kotlin.coroutines.CoroutineContext
-import kotlin.internal.InlineOnly
+import kotlin.coroutines.*
+import kotlin.wasm.internal.*
/**
* Starts an unintercepted coroutine without a receiver and with result type [T] and executes it until its first suspension.
@@ -22,10 +20,11 @@ import kotlin.internal.InlineOnly
* This function is designed to be used from inside of [suspendCoroutineUninterceptedOrReturn] to resume the execution of the suspended
* coroutine using a reference to the suspending function.
*/
-@SinceKotlin("1.3")
+@Suppress("UNCHECKED_CAST")
+@kotlin.internal.InlineOnly
public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrReturn(
completion: Continuation
-): Any? = TODO("Wasm stdlib: Coroutines intrinsics")
+): Any? = startCoroutineUninterceptedOrReturnIntrinsic0(this, completion)
/**
* Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
@@ -39,29 +38,81 @@ public actual inline fun (suspend () -> T).startCoroutineUninterceptedOrRetu
* This function is designed to be used from inside of [suspendCoroutineUninterceptedOrReturn] to resume the execution of the suspended
* coroutine using a reference to the suspending function.
*/
-@SinceKotlin("1.3")
+@Suppress("UNCHECKED_CAST")
+@kotlin.internal.InlineOnly
public actual inline fun (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
completion: Continuation
-): Any? = TODO("Wasm stdlib: Coroutines intrinsics")
+): Any? = startCoroutineUninterceptedOrReturnIntrinsic1(this, receiver, completion)
-@InlineOnly
+@Suppress("UNCHECKED_CAST")
+@kotlin.internal.InlineOnly
internal actual inline fun (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn(
receiver: R,
param: P,
completion: Continuation
-): Any? = TODO("Wasm stdlib: Coroutines intrinsics")
+): Any? = startCoroutineUninterceptedOrReturnIntrinsic2(this, receiver, param, completion)
-@SinceKotlin("1.3")
+/**
+ * Creates unintercepted coroutine without receiver and with result type [T].
+ * This function creates a new, fresh instance of suspendable computation every time it is invoked.
+ *
+ * To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
+ * The [completion] continuation is invoked when coroutine completes with result or exception.
+ *
+ * This function returns unintercepted continuation.
+ * Invocation of `resume(Unit)` starts coroutine immediately in the invoker's call stack without going through the
+ * [ContinuationInterceptor] that might be present in the completion's [CoroutineContext].
+ * It is the invoker's responsibility to ensure that a proper invocation context is established.
+ * Note that [completion] of this function may get invoked in an arbitrary context.
+ *
+ * [Continuation.intercepted] can be used to acquire the intercepted continuation.
+ * Invocation of `resume(Unit)` on intercepted continuation guarantees that execution of
+ * both the coroutine and [completion] happens in the invocation context established by
+ * [ContinuationInterceptor].
+ *
+ * Repeated invocation of any resume function on the resulting continuation corrupts the
+ * state machine of the coroutine and may result in arbitrary behaviour or exception.
+ */
+@Suppress("UNCHECKED_CAST")
public actual fun (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation
-): Continuation = TODO("Wasm stdlib: Coroutines intrinsics")
+): Continuation {
+ return createCoroutineFromSuspendFunction(completion) {
+ this.startCoroutineUninterceptedOrReturn(completion)
+ }
+}
-@SinceKotlin("1.3")
+/**
+ * Creates unintercepted coroutine with receiver type [R] and result type [T].
+ * This function creates a new, fresh instance of suspendable computation every time it is invoked.
+ *
+ * To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
+ * The [completion] continuation is invoked when coroutine completes with result or exception.
+ *
+ * This function returns unintercepted continuation.
+ * Invocation of `resume(Unit)` starts coroutine immediately in the invoker's call stack without going through the
+ * [ContinuationInterceptor] that might be present in the completion's [CoroutineContext].
+ * It is the invoker's responsibility to ensure that a proper invocation context is established.
+ * Note that [completion] of this function may get invoked in an arbitrary context.
+ *
+ * [Continuation.intercepted] can be used to acquire the intercepted continuation.
+ * Invocation of `resume(Unit)` on intercepted continuation guarantees that execution of
+ * both the coroutine and [completion] happens in the invocation context established by
+ * [ContinuationInterceptor].
+ *
+ * Repeated invocation of any resume function on the resulting continuation corrupts the
+ * state machine of the coroutine and may result in arbitrary behaviour or exception.
+ */
+@Suppress("UNCHECKED_CAST")
public actual fun (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R,
completion: Continuation
-): Continuation = TODO("Wasm stdlib: Coroutines intrinsics")
+): Continuation {
+ return createCoroutineFromSuspendFunction(completion) {
+ this.startCoroutineUninterceptedOrReturn(receiver, completion)
+ }
+}
/**
* Intercepts this continuation with [ContinuationInterceptor].
@@ -72,5 +123,18 @@ public actual fun (suspend R.() -> T).createCoroutineUnintercepted(
*
* If this function is invoked on other [Continuation] instances it returns `this` continuation unchanged.
*/
-@SinceKotlin("1.3")
-public actual fun Continuation.intercepted(): Continuation = TODO("Wasm stdlib: Coroutines intrinsics")
+public actual fun Continuation.intercepted(): Continuation =
+ (this as? CoroutineImpl)?.intercepted() ?: this
+
+@Suppress("UNCHECKED_CAST")
+private inline fun createCoroutineFromSuspendFunction(
+ completion: Continuation,
+ crossinline block: () -> Any?
+): Continuation {
+ return object : CoroutineImpl(completion as Continuation) {
+ override fun doResume(): Any? {
+ exception?.let { throw it }
+ return block()
+ }
+ }
+}
\ No newline at end of file