[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:
@@ -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>>()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user