Move Js suspend functions lowerings to common code

This commit is contained in:
Pavel Kunyavskiy
2022-09-02 12:30:26 +02:00
committed by Space Team
parent 7d8636aac4
commit 8886e1b8b4
14 changed files with 130 additions and 95 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.
*/
@@ -243,6 +243,8 @@ class JsIrBackendContext(
override val getContinuation = symbolTable.referenceSimpleFunction(getJsInternalFunction("getContinuation"))
override val continuationClass = context.coroutineSymbols.continuationClass
override val coroutineContextGetter =
symbolTable.referenceSimpleFunction(context.coroutineSymbols.coroutineContextProperty.getter!!)
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.
*/
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToLocalSuspendFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesExtractionFromInlineFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineFunctionsLowering
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.
*/
@@ -31,9 +31,6 @@ class JsMapping : DefaultMapping() {
val fieldToEnumEntry = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrField, IrEnumEntry>()
val enumClassToInitEntryInstancesFun = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
val suspendFunctionsToFunctionWithContinuations =
DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
val suspendArityStore = DefaultDelegateFactory.newDeclarationToDeclarationCollectionMapping<IrClass, Collection<IrSimpleFunction>>()
// Wasm mappings
@@ -1,79 +1,27 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.common.lower.coroutines.AbstractAddContinuationToFunctionCallsLowering
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.irCall
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
/**
* Add continuation to suspend function calls. Requires [AddContinuationToLocalSuspendFunctionsLowering] and
* 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()
class AddContinuationToFunctionCallsLowering(override val context: JsCommonBackendContext) : AbstractAddContinuationToFunctionCallsLowering() {
override fun IrSimpleFunction.getContinuationParameter(): IrValueParameter =
if (overriddenSymbols.any {
it.owner.name.asString() == "doResume" && it.owner.parent == context.coroutineSymbols.coroutineImpl.owner
}
) {
dispatchReceiverParameter!!
} else {
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
// TODO: investigate why mapping might be unavailable for SuspendFunction4.invoke
val newFun: IrSimpleFunction = oldFun.getOrCreateFunctionWithContinuationStub(context)
return irCall(
expression,
newFun.symbol,
newReturnType = newFun.returnType,
newSuperQualifierSymbol = expression.superQualifierSymbol
).also {
it.putValueArgument(it.valueArgumentsCount - 1, getContinuation())
}
}
})
}
}
}
@@ -1,132 +0,0 @@
/*
* 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.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.getOrPut
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.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irReturnUnit
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrType
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
}
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 = IrDeclarationOrigin.LOWERED_SUSPEND_FUNCTION
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) }
val mapping = mutableMapOf<IrValueSymbol, IrValueSymbol>()
valueParameters.forEach { mapping[it.symbol] = function.valueParameters[it.index].symbol }
val remapper = ValueRemapper(mapping)
function.valueParameters.forEach { it.defaultValue = it.defaultValue?.transform(remapper, null) }
function.addValueParameter(
"\$cont",
continuationType(context).substitute(substitutionMap),
IrDeclarationOrigin.CONTINUATION
)
}
}
private fun IrFunction.continuationType(context: JsCommonBackendContext): IrType {
return context.coroutineSymbols.continuationClass.typeWith(returnType)
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.
*/
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
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.backend.common.lower.coroutines.loweredSuspendFunctionReturnType
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.*
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2022 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.
*/
@@ -111,6 +111,3 @@ class LiveLocalsTransformer(
}
}
}
fun loweredSuspendFunctionReturnType(function: IrFunction, irBuiltIns: IrBuiltIns): IrType =
if (function.returnType.isNullable()) irBuiltIns.anyNType else irBuiltIns.anyType