JVM_IR: Move suspend functions into view transformation to codegen phase
Support suspend functions with default arguments.
This commit is contained in:
@@ -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>()
|
||||
|
||||
|
||||
+106
-4
@@ -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)
|
||||
}
|
||||
}
|
||||
+6
-8
@@ -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)
|
||||
|
||||
+15
-10
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
+2
-5
@@ -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,
|
||||
|
||||
+12
-128
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
@@ -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
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
Reference in New Issue
Block a user