JVM_IR: Move suspend functions into view transformation to codegen phase

Support suspend functions with default arguments.
This commit is contained in:
Ilmir Usmanov
2019-09-11 22:06:45 +03:00
parent 6b36833ee2
commit 2ec3417e0e
17 changed files with 146 additions and 179 deletions
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2019 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.backend.common.ir
@@ -528,7 +517,7 @@ fun createStaticFunctionWithReceivers(
modality,
oldFunction.returnType,
isInline = oldFunction.isInline,
isExternal = false, isTailrec = false, isSuspend = false
isExternal = false, isTailrec = false, isSuspend = oldFunction.isSuspend
).apply {
descriptor.bind(this)
parent = irParent
@@ -83,6 +83,7 @@ class JvmBackendContext(
val suspendFunctionContinuations = mutableMapOf<IrFunction, IrClass>()
val suspendLambdaToOriginalFunctionMap = mutableMapOf<IrClass, IrFunction>()
val continuationClassBuilders = mutableMapOf<IrClass, ClassBuilder>()
val suspendFunctionViews = mutableMapOf<IrFunction, IrFunction>()
val staticDefaultStubs = mutableMapOf<IrFunctionSymbol, IrFunction>()
@@ -5,18 +5,46 @@
package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.common.descriptors.WrappedFunctionDescriptorWithContainerSource
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME
import org.jetbrains.kotlin.config.coroutinesPackageFqName
import org.jetbrains.kotlin.config.isReleaseCoroutines
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.explicitParameters
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.org.objectweb.asm.MethodVisitor
fun generateStateMachineForNamedFunction(
internal fun generateStateMachineForNamedFunction(
irFunction: IrFunction,
classCodegen: ClassCodegen,
methodVisitor: MethodVisitor,
@@ -44,7 +72,7 @@ fun generateStateMachineForNamedFunction(
)
}
fun generateStateMachineForLambda(
internal fun generateStateMachineForLambda(
classCodegen: ClassCodegen,
methodVisitor: MethodVisitor,
access: Int,
@@ -66,5 +94,79 @@ fun generateStateMachineForLambda(
)
}
fun IrFunction.isInvokeSuspendOfLambda(context: JvmBackendContext): Boolean =
name.asString() == INVOKE_SUSPEND_METHOD_NAME && parent in context.suspendLambdaToOriginalFunctionMap
internal fun IrFunction.isInvokeSuspendOfLambda(context: JvmBackendContext): Boolean =
name.asString() == INVOKE_SUSPEND_METHOD_NAME && parent in context.suspendLambdaToOriginalFunctionMap
internal fun IrFunction.isInvokeSuspendOfContinuation(context: JvmBackendContext): Boolean =
name.asString() == INVOKE_SUSPEND_METHOD_NAME && parentAsClass in context.suspendFunctionContinuations.values
// Transform `suspend fun foo(params): RetType` into `fun foo(params, $completion: Continuation<RetType>): Any?`
// the result is called 'view', just to be consistent with old backend.
internal fun IrFunction.getOrCreateSuspendFunctionViewIfNeeded(context: JvmBackendContext): IrFunction {
if (!isSuspend) return this
// TODO: Optimize
if (context.suspendFunctionViews.values.contains(this)) return this
return if (isSuspend) context.suspendFunctionViews.getOrPut(this) { suspendFunctionView(context) } else this
}
private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFunction {
require(this.isSuspend && this is IrSimpleFunction)
val originalDescriptor = this.descriptor
val descriptor =
if (originalDescriptor is DescriptorWithContainerSource && originalDescriptor.containerSource != null)
WrappedFunctionDescriptorWithContainerSource(originalDescriptor.containerSource!!)
else
WrappedSimpleFunctionDescriptor(sourceElement = originalDescriptor.source)
return IrFunctionImpl(
startOffset, endOffset, origin, IrSimpleFunctionSymbolImpl(descriptor),
name, visibility, modality, context.irBuiltIns.anyNType,
isInline, isExternal, isTailrec, isSuspend
).also {
descriptor.bind(it)
it.parent = parent
it.copyTypeParametersFrom(this)
it.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(it)
it.extensionReceiverParameter = extensionReceiverParameter?.copyTo(it)
valueParameters.mapTo(it.valueParameters) { p -> p.copyTo(it) }
it.addValueParameter(
SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, context.getTopLevelClass(
context.state.languageVersionSettings.coroutinesPackageFqName().child(
Name.identifier("Continuation")
)
).createType(false, listOf(makeTypeProjection(returnType, Variance.INVARIANT)))
)
val valueParametersMapping = explicitParameters.zip(it.explicitParameters).toMap()
it.body = body?.deepCopyWithSymbols(this)
it.body?.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrGetValue =
valueParametersMapping[expression.symbol.owner]?.let { newParam ->
expression.run { IrGetValueImpl(startOffset, endOffset, type, newParam.symbol, origin) }
} ?: expression
override fun visitCall(expression: IrCall): IrExpression {
if (!expression.isSuspend) return super.visitCall(expression)
return super.visitCall(expression.createView(context, it))
}
})
}
}
internal fun IrCall.createView(context: JvmBackendContext, caller: IrFunction): IrCall {
if (!isSuspend) return this
val view = (symbol.owner as IrSimpleFunction).getOrCreateSuspendFunctionViewIfNeeded(context)
if (view == symbol.owner) return this
return IrCallImpl(startOffset, endOffset, view.returnType, view.symbol).also {
it.copyTypeArgumentsFrom(this)
it.dispatchReceiver = dispatchReceiver
for (i in 0 until valueArgumentsCount) {
it.putValueArgument(i, getValueArgument(i))
}
val continuationParameter =
if (caller.isInvokeSuspendOfLambda(context) || caller.isInvokeSuspendOfContinuation(context)) caller.dispatchReceiverParameter!!
else caller.valueParameters.last()
val continuation = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, continuationParameter.symbol)
it.putValueArgument(valueArgumentsCount, continuation)
}
}
@@ -298,6 +298,7 @@ class ExpressionCodegen(
visitStatementContainer(expression, data).coerce(expression.type)
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
val expression = if (expression is IrCall) expression.createView(context, irFunction) else expression
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
?.invoke(expression, this, data)?.let { return it.coerce(expression.type) }
@@ -333,7 +334,7 @@ class ExpressionCodegen(
}
expression.descriptor is ConstructorDescriptor ->
throw AssertionError("IrCall with ConstructorDescriptor: ${expression.javaClass.simpleName}")
callee.isSuspend && !irFunction.isInvokeSuspendInContinuation() ->
callee.isSuspend && !irFunction.isInvokeSuspendOfContinuation(classCodegen.context) ->
addInlineMarker(mv, isStartNotEnd = true)
}
@@ -359,13 +360,13 @@ class ExpressionCodegen(
expression.markLineNumber(true)
// Do not generate redundant markers in continuation class.
if (callee.isSuspend && !irFunction.isInvokeSuspendInContinuation()) {
if (callee.isSuspend && !irFunction.isInvokeSuspendOfContinuation(classCodegen.context)) {
addSuspendMarker(mv, isStartNotEnd = true)
}
callGenerator.genCall(callable, this, expression)
if (callee.isSuspend && !irFunction.isInvokeSuspendInContinuation()) {
if (callee.isSuspend && !irFunction.isInvokeSuspendOfContinuation(classCodegen.context)) {
addSuspendMarker(mv, isStartNotEnd = false)
addInlineMarker(mv, isStartNotEnd = false)
}
@@ -389,9 +390,6 @@ class ExpressionCodegen(
}
}
private fun IrFunction.isInvokeSuspendInContinuation(): Boolean =
name.asString() == INVOKE_SUSPEND_METHOD_NAME && parentAsClass in classCodegen.context.suspendFunctionContinuations.values
override fun visitVariable(declaration: IrVariable, data: BlockInfo): PromisedValue {
val varType = typeMapper.mapType(declaration)
val index = frameMap.enter(declaration.symbol, varType)
@@ -532,9 +530,9 @@ class ExpressionCodegen(
override fun visitReturn(expression: IrReturn, data: BlockInfo): PromisedValue {
val returnTarget = expression.returnTargetSymbol.owner
val owner =
returnTarget as? IrFunction
(returnTarget as? IrFunction
?: (returnTarget as? IrReturnableBlock)?.inlineFunctionSymbol?.owner
?: error("Unsupported IrReturnTarget: $returnTarget")
?: error("Unsupported IrReturnTarget: $returnTarget")).getOrCreateSuspendFunctionViewIfNeeded(context)
//TODO: should be owner != irFunction
val isNonLocalReturn =
methodSignatureMapper.mapFunctionName(owner) != methodSignatureMapper.mapFunctionName(irFunction)
@@ -43,9 +43,10 @@ open class FunctionCodegen(
}
private fun doGenerate(): JvmMethodGenericSignature {
val signature = classCodegen.methodSignatureMapper.mapSignatureWithGeneric(irFunction)
val functionView = irFunction.getOrCreateSuspendFunctionViewIfNeeded(context)
val signature = classCodegen.methodSignatureMapper.mapSignatureWithGeneric(functionView)
val flags = calculateMethodFlags(irFunction.isStatic)
val flags = calculateMethodFlags(functionView.isStatic)
var methodVisitor = createMethod(flags, signature)
val hasSyntheticFlag = flags.and(Opcodes.ACC_SYNTHETIC) != 0
@@ -81,15 +82,17 @@ open class FunctionCodegen(
?: context.suspendLambdaToOriginalFunctionMap[irFunction.parent]?.symbol?.descriptor?.psiElement) as? KtElement
val continuationClassBuilder = context.continuationClassBuilders[irClass]
methodVisitor = when {
irFunction.isSuspend -> generateStateMachineForNamedFunction(
irFunction, classCodegen, methodVisitor, flags, signature, continuationClassBuilder, element!!
)
// We do not generate continuation and state-machine for synthetic accessors, in a sence, they are tail-call
irFunction.isSuspend && irFunction.origin != JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR ->
generateStateMachineForNamedFunction(
irFunction, classCodegen, methodVisitor, flags, signature, continuationClassBuilder, element!!
)
irFunction.isInvokeSuspendOfLambda(context) -> generateStateMachineForLambda(
classCodegen, methodVisitor, flags, signature, element!!
)
else -> methodVisitor
}
ExpressionCodegen(irFunction, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, isInlineLambda).generate()
ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, isInlineLambda).generate()
methodVisitor.visitMaxs(-1, -1)
continuationClassBuilder?.done()
}
@@ -182,15 +185,17 @@ open class FunctionCodegen(
private fun createFrameMapWithReceivers(signature: JvmMethodSignature): IrFrameMap {
val frameMap = IrFrameMap()
val functionView = irFunction.getOrCreateSuspendFunctionViewIfNeeded(context)
if (irFunction is IrConstructor) {
frameMap.enterDispatchReceiver(irFunction.constructedClass.thisReceiver!!)
} else if (irFunction.dispatchReceiverParameter != null) {
frameMap.enterDispatchReceiver(irFunction.dispatchReceiverParameter!!)
} else if (functionView.dispatchReceiverParameter != null) {
frameMap.enterDispatchReceiver(functionView.dispatchReceiverParameter!!)
}
for (parameter in signature.valueParameters) {
if (parameter.kind == JvmMethodParameterKind.RECEIVER) {
val receiverParameter = irFunction.extensionReceiverParameter
val receiverParameter = functionView.extensionReceiverParameter
if (receiverParameter != null) {
frameMap.enter(receiverParameter, classCodegen.typeMapper.mapType(receiverParameter))
} else {
@@ -201,7 +206,7 @@ open class FunctionCodegen(
}
}
for (parameter in irFunction.valueParameters) {
for (parameter in functionView.valueParameters) {
frameMap.enter(parameter, classCodegen.typeMapper.mapType(parameter.type))
}
@@ -30,11 +30,8 @@ import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.getJvmMethodNameIfSpecial
import org.jetbrains.kotlin.load.java.getOverriddenBuiltinReflectingJvmDescriptor
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.load.kotlin.forceSingleValueParameterBoxing
import org.jetbrains.kotlin.load.kotlin.getJvmModuleNameForDeserializedDescriptor
@@ -295,7 +292,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
}
fun mapToCallableMethod(expression: IrFunctionAccessExpression): IrCallableMethod {
val callee = expression.symbol.owner
val callee = expression.symbol.owner.getOrCreateSuspendFunctionViewIfNeeded(context)
val calleeParent = callee.parent
if (calleeParent !is IrClass) {
// Non-class parent is only possible for intrinsics created in IrBuiltIns, such as dataClassArrayMemberHashCode. In that case,
@@ -6,13 +6,10 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedFunctionDescriptorWithContainerSource
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
@@ -26,11 +23,9 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
@@ -38,23 +33,19 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
// TODO: Split into several lowerings, merge with ones in JS and Native
internal val addContinuationPhase = makeIrFilePhase(
::AddContinuationLowering,
"AddContinuation",
"Add continuation classes and continuation parameter to suspend functions and suspend calls"
"Add continuation classes to suspend functions and transform suspend lambdas into continuations"
)
private object CONTINUATION_CLASS : IrDeclarationOriginImpl("CONTINUATION_CLASS")
private class AddContinuationLowering(private val context: JvmBackendContext) : FileLoweringPass {
private val transformedSuspendFunctionsCache = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
private val continuation by lazy {
context.getTopLevelClass(context.state.languageVersionSettings.coroutinesPackageFqName().child(Name.identifier("Continuation")))
}
@@ -67,7 +58,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
override fun lower(irFile: IrFile) {
val suspendLambdas = markSuspendLambdas(irFile)
val suspendFunctions = addContinuationParameter(irFile, suspendLambdas.map { it.function }.toSet())
val suspendFunctions = markSuspendFunctions(irFile, suspendLambdas.map { it.function }.toSet())
for (lambda in suspendLambdas) {
generateContinuationClassForLambda(lambda)
}
@@ -385,49 +376,32 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
it.dispatchReceiver = capturedThisField?.let { irField ->
irGetField(irGet(function.dispatchReceiverParameter!!), irField)
}
for (i in irFunction.valueParameters.dropLast(1).indices) {
for (i in irFunction.valueParameters.indices) {
// TODO: also support primitives
it.putValueArgument(i, irNull())
}
it.putValueArgument(irFunction.valueParameters.size - 1, irGet(function.dispatchReceiverParameter!!))
})
}
}
}
private fun addContinuationParameter(irFile: IrFile, suspendLambdas: Set<IrFunction>): Set<IrFunction> {
val views = hashSetOf<IrFunction>()
private fun markSuspendFunctions(irFile: IrFile, suspendLambdas: Set<IrFunction>): Set<IrFunction> {
val result = hashSetOf<IrFunction>()
// Collect all suspend functions
irFile.acceptVoid(object : IrElementVisitorVoid {
irFile.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
declaration.transformDeclarationsFlat { element ->
if (element is IrSimpleFunction && element.isSuspend && element !in suspendLambdas) {
listOf(element.getOrCreateView().also { views.add(it) })
} else null
override fun visitFunction(declaration: IrFunction) {
super.visitFunction(declaration)
if (declaration.isSuspend && declaration !in suspendLambdas) {
result.add(declaration)
}
}
})
// fix return targets
for (view in views) {
view.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitReturn(expression: IrReturn): IrExpression {
val owner = expression.returnTargetSymbol.owner as? IrSimpleFunction
?: return super.visitReturn(expression)
val ownerView = owner.getOrCreateView()
if (ownerView == owner) return super.visitReturn(expression)
val result =
IrReturnImpl(expression.startOffset, expression.endOffset, ownerView.returnType, ownerView.symbol, expression.value)
return super.visitReturn(result)
}
})
}
return views
return result
}
private fun markSuspendLambdas(irElement: IrElement): List<SuspendLambdaInfo> {
@@ -466,99 +440,9 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
functionStack.pop()
return res
}
private fun getContinuationFromCaller(): IrGetValue? {
val caller = functionStack.peek()!!
val continuationParameter =
if (caller.isSuspend) caller.valueParameters.last().symbol
else if (caller.name.asString() == INVOKE_SUSPEND_METHOD_NAME && caller.parent in context.suspendLambdaToOriginalFunctionMap)
caller.dispatchReceiverParameter!!.symbol
else return null
return IrGetValueImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
continuationParameter
)
}
override fun visitCall(expression: IrCall): IrExpression {
if (!expression.isSuspend) return super.visitCall(expression)
val view = (expression.symbol.owner as IrSimpleFunction).getOrCreateView()
val res = IrCallImpl(expression.startOffset, expression.endOffset, view.returnType, view.symbol).apply {
copyTypeArgumentsFrom(expression)
dispatchReceiver = expression.dispatchReceiver
for (i in 0 until expression.valueArgumentsCount) {
putValueArgument(i, expression.getValueArgument(i))
}
val continuation = getContinuationFromCaller() ?: error(
"Cannot get continuation from context for a suspend call.\n" +
"Caller: ${ir2string(functionStack.peek())}\n" +
"Callee: ${ir2string(expression)}"
)
putValueArgument(expression.valueArgumentsCount, continuation)
}
return super.visitCall(res)
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
if (!expression.isSuspend) return super.visitFunctionReference(expression)
val view = (expression.symbol.owner as IrSimpleFunction).getOrCreateView()
val res = IrFunctionReferenceImpl(
expression.startOffset,
expression.endOffset,
expression.type,
view.symbol,
view.descriptor,
expression.typeArgumentsCount,
expression.origin
).apply {
copyTypeArgumentsFrom(expression)
dispatchReceiver = expression.dispatchReceiver
for (i in 0 until expression.valueArgumentsCount) {
putValueArgument(i, expression.getValueArgument(i))
}
}
return super.visitFunctionReference(res)
}
})
}
private fun IrSimpleFunction.getOrCreateView(): IrSimpleFunction =
transformedSuspendFunctionsCache.getOrPut(this) {
// Copy source element, so we can check for suspend calls in monitor during state-machine generation
val originalDescriptor = this.descriptor
val descriptor =
if (originalDescriptor is DescriptorWithContainerSource && originalDescriptor.containerSource != null)
WrappedFunctionDescriptorWithContainerSource(originalDescriptor.containerSource!!)
else
WrappedSimpleFunctionDescriptor(sourceElement = originalDescriptor.source)
IrFunctionImpl(
startOffset, endOffset, origin, IrSimpleFunctionSymbolImpl(descriptor),
name, visibility, modality, context.irBuiltIns.anyType,
isInline, isExternal, isTailrec, isSuspend
).also {
descriptor.bind(it)
it.parent = parent
it.copyTypeParametersFrom(this)
it.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(it)
it.extensionReceiverParameter = extensionReceiverParameter?.copyTo(it)
valueParameters.mapTo(it.valueParameters) { p -> p.copyTo(it) }
it.addCompletionValueParameter()
// Fix links to parameters
val valueParametersMapping = explicitParameters.zip(it.explicitParameters).toMap()
it.body = body?.deepCopyWithSymbols(this)
it.body?.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue) =
valueParametersMapping[expression.symbol.owner]?.let { newParam ->
expression.run { IrGetValueImpl(startOffset, endOffset, type, newParam.symbol, origin) }
} ?: expression
})
}
}
private class SuspendLambdaInfo(val function: IrFunction, val arity: Int, val reference: IrFunctionReference) {
lateinit var constructor: IrConstructor
}
@@ -155,7 +155,7 @@ private class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElem
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR
name = source.accessorName()
visibility = Visibilities.PUBLIC
isSuspend = false // do not generate state-machine for synthetic accessors
isSuspend = this@makeSimpleFunctionAccessor.isSuspend // synthetic accessors of suspend functions are handled in codegen
}.also { accessor ->
// Find the right container to insert the accessor. Simply put, when we call a function on a class A,
// we also need to put its accessor into A. However, due to the way that calls are implemented in the
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,5 +1,4 @@
// KJS_WITH_FULL_RUNTIME
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,5 +1,4 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST