[Wasm] Support coroutines
- Reuse JS IR Suspend function lowering
- Fix types
- Disable reinterpretCast-based optimization for inline
classes
- Add basic support to Wasm stdlib
- Explicitly transform suspend functions into regular functions
with continuations
- Clean suspend function handling from JS IR codegen
This commit is contained in:
+1
@@ -18,6 +18,7 @@
|
||||
<w>sqrt</w>
|
||||
<w>testsuite</w>
|
||||
<w>uninstantiable</w>
|
||||
<w>unintercepted</w>
|
||||
<w>unlinkable</w>
|
||||
<w>vtable</w>
|
||||
<w>wabt</w>
|
||||
|
||||
@@ -94,9 +94,10 @@ private fun buildRoots(modules: Iterable<IrModuleFragment>, 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
|
||||
}
|
||||
}
|
||||
|
||||
+98
-1
@@ -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 <T> 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<IrSimpleFunction>()
|
||||
.atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
|
||||
?: continuationClass.owner.declarations.filterIsInstance<IrProperty>()
|
||||
.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("<get-context>")
|
||||
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<SimpleFunctionDescriptor> =
|
||||
memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList()
|
||||
|
||||
interface InlineClassesUtils {
|
||||
fun isTypeInlined(type: IrType): Boolean
|
||||
|
||||
|
||||
+9
-69
@@ -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("<get-context>")
|
||||
|
||||
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<JsIrBackendContext>(this, irModuleFragment) {
|
||||
override val symbols = object : Symbols<JsIrBackendContext>(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<IrSimpleFunction>()
|
||||
.atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
|
||||
?: continuationClass.owner.declarations.filterIsInstance<IrProperty>()
|
||||
.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<IrProperty>()
|
||||
}
|
||||
@@ -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<SimpleFunctionDescriptor> =
|
||||
memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList()
|
||||
|
||||
private fun findProperty(memberScope: MemberScope, name: Name): List<PropertyDescriptor> =
|
||||
memberScope.getContributedVariables(name, NoLookupLocation.FROM_BACKEND).toList()
|
||||
|
||||
@@ -442,7 +385,4 @@ class JsIrBackendContext(
|
||||
/*TODO*/
|
||||
print(message)
|
||||
}
|
||||
|
||||
// TODO: investigate if it could be removed
|
||||
fun <T> lazy2(fn: () -> T) = lazy { irFactory.stageController.withInitialIr(fn) }
|
||||
}
|
||||
|
||||
@@ -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<Lowering>(
|
||||
suspendFunctionsLoweringPhase,
|
||||
propertyReferenceLoweringPhase,
|
||||
interopCallableReferenceLoweringPhase,
|
||||
jsSuspendArityStorePhase,
|
||||
addContinuationToNonLocalSuspendFunctionsLoweringPhase,
|
||||
addContinuationToLocalSuspendFunctionsLoweringPhase,
|
||||
addContinuationToFunctionCallsLoweringPhase,
|
||||
returnableBlockLoweringPhase,
|
||||
rangeContainsLoweringPhase,
|
||||
forLoopsLoweringPhase,
|
||||
@@ -855,7 +881,6 @@ private val loweringList = listOf<Lowering>(
|
||||
callsLoweringPhase,
|
||||
cleanupLoweringPhase,
|
||||
validateIrAfterLowering,
|
||||
jsSuspendArityStorePhase
|
||||
)
|
||||
|
||||
// TODO comment? Eliminate ModuleLowering's? Don't filter them here?
|
||||
|
||||
+81
@@ -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())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+122
@@ -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<IrDeclaration>? =
|
||||
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)
|
||||
}
|
||||
+40
-20
@@ -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<JsIrBackendContext>(ctx) {
|
||||
class JsSuspendFunctionsLowering(ctx: JsCommonBackendContext) : AbstractSuspendFunctionsLowering<JsCommonBackendContext>(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<IrValueParameter, IrField>
|
||||
) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-9
@@ -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<IrLoop, LoopBounds>()
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+15
-4
@@ -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<IrElement>) : 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loweredSuspendFunctionReturnType(function: IrFunction, irBuiltIns: IrBuiltIns): IrType =
|
||||
if (function.returnType.isNullable()) irBuiltIns.anyNType else irBuiltIns.anyType
|
||||
+12
-5
@@ -349,12 +349,16 @@ class IrModuleToJsTransformer(
|
||||
return statements
|
||||
}
|
||||
|
||||
private fun generateMainArguments(mainFunction: IrSimpleFunction, rootContext: JsGenerationContext): List<JsExpression> {
|
||||
private fun generateMainArguments(
|
||||
generateArgv: Boolean,
|
||||
generateContinuation: Boolean,
|
||||
rootContext: JsGenerationContext
|
||||
): List<JsExpression> {
|
||||
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<IrModuleFragment>, rootContext: JsGenerationContext): List<JsStatement> {
|
||||
// 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()
|
||||
}
|
||||
|
||||
|
||||
+2
-14
@@ -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)
|
||||
|
||||
+3
-6
@@ -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() =
|
||||
|
||||
-18
@@ -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
|
||||
}
|
||||
|
||||
+41
-21
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
+30
-13
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
+6
-4
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ class JsMapping(val state: JsMappingState) : DefaultMapping(state) {
|
||||
val enumEntryToCorrespondingField = state.newDeclarationToDeclarationMapping<IrEnumEntry, IrField>()
|
||||
val enumClassToInitEntryInstancesFun = state.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
|
||||
val suspendFunctionsToFunctionWithContinuations =
|
||||
state.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
|
||||
|
||||
val suspendArityStore = state.newDeclarationToDeclarationCollectionMapping<IrClass, Collection<IrSimpleFunction>>()
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ internal fun <T> getContinuation(): Continuation<T> { throw Exception("Implement
|
||||
// Do we really need this intrinsic in JS?
|
||||
|
||||
@PublishedApi
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal suspend fun <T> returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T {
|
||||
throw Exception("Implemented as intrinsic")
|
||||
return argument as T
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
|
||||
@@ -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 <T> getContinuation(): Continuation<T> =
|
||||
implementedAsIntrinsic
|
||||
|
||||
@PublishedApi
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal suspend fun <T> returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T =
|
||||
argument as T
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T> interceptContinuationIfNeeded(
|
||||
context: CoroutineContext,
|
||||
continuation: Continuation<T>
|
||||
) = context[ContinuationInterceptor]?.interceptContinuation(continuation) ?: continuation
|
||||
|
||||
|
||||
@PublishedApi
|
||||
internal suspend fun getCoroutineContext(): CoroutineContext = getContinuation<Any?>().context
|
||||
|
||||
@PublishedApi
|
||||
internal suspend fun <T> suspendCoroutineUninterceptedOrReturn(block: (Continuation<T>) -> Any?): T =
|
||||
returnIfSuspended<T>(block(getContinuation<T>()))
|
||||
|
||||
@ExcludedFromCodegen
|
||||
@PublishedApi
|
||||
internal fun <T> startCoroutineUninterceptedOrReturnIntrinsic0(
|
||||
f: (suspend () -> T),
|
||||
completion: Continuation<T>
|
||||
): Any? {
|
||||
implementedAsIntrinsic
|
||||
}
|
||||
|
||||
@ExcludedFromCodegen
|
||||
@PublishedApi
|
||||
internal fun <R, T> startCoroutineUninterceptedOrReturnIntrinsic1(
|
||||
f: (suspend R.() -> T),
|
||||
receiver: R,
|
||||
completion: Continuation<T>
|
||||
): Any? {
|
||||
implementedAsIntrinsic
|
||||
}
|
||||
|
||||
@ExcludedFromCodegen
|
||||
@PublishedApi
|
||||
internal fun <R, P, T> startCoroutineUninterceptedOrReturnIntrinsic2(
|
||||
f: (suspend R.(P) -> T),
|
||||
receiver: R,
|
||||
param: P,
|
||||
completion: Continuation<T>
|
||||
): Any? {
|
||||
implementedAsIntrinsic
|
||||
}
|
||||
@@ -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<Any?>?) : Continuation<Any?> {
|
||||
protected var state = 0
|
||||
protected var exceptionState = 0
|
||||
protected var result: Any? = null
|
||||
protected var exception: Throwable? = null
|
||||
protected var finallyPath: Array<Int>? = null
|
||||
|
||||
private val _context: CoroutineContext? = resultContinuation?.context
|
||||
|
||||
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()
|
||||
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<Unit> {
|
||||
throw UnsupportedOperationException("create(Continuation) has not been overridden")
|
||||
}
|
||||
|
||||
public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> {
|
||||
throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden")
|
||||
}
|
||||
}
|
||||
|
||||
internal object CompletedContinuation : Continuation<Any?> {
|
||||
override val context: CoroutineContext
|
||||
get() = error("This continuation is already complete")
|
||||
|
||||
override fun resumeWith(result: Result<Any?>) {
|
||||
error("This continuation is already complete")
|
||||
}
|
||||
|
||||
override fun toString(): String = "This continuation is already complete"
|
||||
}
|
||||
@@ -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<in T> : Continuation<T> {
|
||||
actual internal constructor(delegate: Continuation<T>, initialResult: Any?) { TODO("Wasm stdlib: Coroutines") }
|
||||
internal actual class SafeContinuation<in T>
|
||||
internal actual constructor(
|
||||
private val delegate: Continuation<T>,
|
||||
initialResult: Any?
|
||||
) : Continuation<T> {
|
||||
@PublishedApi
|
||||
internal actual constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
|
||||
|
||||
public actual override val context: CoroutineContext
|
||||
get() = delegate.context
|
||||
|
||||
private var result: Any? = initialResult
|
||||
|
||||
public actual override fun resumeWith(result: Result<T>) {
|
||||
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<T>) { 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<T>): 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
|
||||
completion: Continuation<T>
|
||||
): 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 <T> (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 <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
|
||||
receiver: R,
|
||||
completion: Continuation<T>
|
||||
): Any? = TODO("Wasm stdlib: Coroutines intrinsics")
|
||||
): Any? = startCoroutineUninterceptedOrReturnIntrinsic1(this, receiver, completion)
|
||||
|
||||
@InlineOnly
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@kotlin.internal.InlineOnly
|
||||
internal actual inline fun <R, P, T> (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn(
|
||||
receiver: R,
|
||||
param: P,
|
||||
completion: Continuation<T>
|
||||
): 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 <T> (suspend () -> T).createCoroutineUnintercepted(
|
||||
completion: Continuation<T>
|
||||
): Continuation<Unit> = TODO("Wasm stdlib: Coroutines intrinsics")
|
||||
): Continuation<Unit> {
|
||||
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 <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
|
||||
receiver: R,
|
||||
completion: Continuation<T>
|
||||
): Continuation<Unit> = TODO("Wasm stdlib: Coroutines intrinsics")
|
||||
): Continuation<Unit> {
|
||||
return createCoroutineFromSuspendFunction(completion) {
|
||||
this.startCoroutineUninterceptedOrReturn(receiver, completion)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts this continuation with [ContinuationInterceptor].
|
||||
@@ -72,5 +123,18 @@ public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
|
||||
*
|
||||
* If this function is invoked on other [Continuation] instances it returns `this` continuation unchanged.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> = TODO("Wasm stdlib: Coroutines intrinsics")
|
||||
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
|
||||
(this as? CoroutineImpl)?.intercepted() ?: this
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private inline fun <T> createCoroutineFromSuspendFunction(
|
||||
completion: Continuation<T>,
|
||||
crossinline block: () -> Any?
|
||||
): Continuation<Unit> {
|
||||
return object : CoroutineImpl(completion as Continuation<Any?>) {
|
||||
override fun doResume(): Any? {
|
||||
exception?.let { throw it }
|
||||
return block()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user