diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt index 65d132a97e2..a620a4afdb0 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt @@ -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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index bacf9a4de3d..c46d72f82a6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -83,6 +83,7 @@ class JvmBackendContext( val suspendFunctionContinuations = mutableMapOf() val suspendLambdaToOriginalFunctionMap = mutableMapOf() val continuationClassBuilders = mutableMapOf() + val suspendFunctionViews = mutableMapOf() val staticDefaultStubs = mutableMapOf() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt index ba9ba722f88..96d8178f7a2 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt @@ -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 \ No newline at end of file +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): 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) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 2690979dcdc..ad6e6ee9378 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -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) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index a86a8a757c8..8a78135b121 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -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)) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt index 6eee9b45b65..6dcb259f64c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt @@ -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, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt index 3038ba48a06..6586ab5f839 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt @@ -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() - 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): Set { - val views = hashSetOf() + private fun markSuspendFunctions(irFile: IrFile, suspendLambdas: Set): Set { + val result = hashSetOf() - // 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 { @@ -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 } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index b8c258f6012..feb6867b78a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -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 diff --git a/compiler/testData/codegen/box/coroutines/32defaultParametersInSuspend.kt b/compiler/testData/codegen/box/coroutines/32defaultParametersInSuspend.kt index d3ec2d5a5ab..92c1aa88445 100644 --- a/compiler/testData/codegen/box/coroutines/32defaultParametersInSuspend.kt +++ b/compiler/testData/codegen/box/coroutines/32defaultParametersInSuspend.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/accessorForSuspend.kt b/compiler/testData/codegen/box/coroutines/accessorForSuspend.kt index a3b36b8fa77..e35dfb3e971 100644 --- a/compiler/testData/codegen/box/coroutines/accessorForSuspend.kt +++ b/compiler/testData/codegen/box/coroutines/accessorForSuspend.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt b/compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt index 19cf7557b56..38bc1800523 100644 --- a/compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt +++ b/compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt @@ -1,5 +1,4 @@ // KJS_WITH_FULL_RUNTIME -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt b/compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt index 6587f831cd6..8c018988501 100644 --- a/compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt +++ b/compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt b/compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt index c8b35e16b24..37c31f507fd 100644 --- a/compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt +++ b/compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/overrideDefaultArgument.kt b/compiler/testData/codegen/box/coroutines/overrideDefaultArgument.kt index 2fdfc185c8b..e292198e2be 100644 --- a/compiler/testData/codegen/box/coroutines/overrideDefaultArgument.kt +++ b/compiler/testData/codegen/box/coroutines/overrideDefaultArgument.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/privateInFile.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/privateInFile.kt index 820b7826abe..86f9adb506c 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/privateInFile.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/privateInFile.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/suspendJavaOverrides.kt b/compiler/testData/codegen/box/coroutines/suspendJavaOverrides.kt index e34b18733ad..c035ed1afb0 100644 --- a/compiler/testData/codegen/box/coroutines/suspendJavaOverrides.kt +++ b/compiler/testData/codegen/box/coroutines/suspendJavaOverrides.kt @@ -1,5 +1,4 @@ // TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt b/compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt index 88e0f22472a..dc343d309d8 100644 --- a/compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt +++ b/compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST