JVM_IR: Move suspend function views creation to lowering
Now AddContinuationLowering is responsible for both adding continuation classes to suspend functions and adding continuation parameters to them. Because we cannot create a view if inline suspend function is defined in another file, we generate a stub without body when we encounter call to it. And then, when we lower the file containing the function we add the body. This way we have no unlowered views after the lowering. Thus, after the lowering there should be no suspend function, which are not views, therefore, remove VIEW origins. Because transformations of suspend functions can copy them into another object, use attribute as a key inside function to view map.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,6 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object INLINE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("INLINE_CLASS_GENERATED_IMPL_METHOD")
|
||||
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
|
||||
object GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD : IrDeclarationOriginImpl("GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD", isSynthetic = true)
|
||||
object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW")
|
||||
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true)
|
||||
object INTERFACE_COMPANION_PRIVATE_INSTANCE : IrDeclarationOriginImpl("INTERFACE_COMPANION_PRIVATE_INSTANCE", isSynthetic = true)
|
||||
object POLYMORPHIC_SIGNATURE_INSTANTIATION : IrDeclarationOriginImpl("POLYMORPHIC_SIGNATURE_INSTANTIATION", isSynthetic = true)
|
||||
@@ -43,6 +42,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object SUSPEND_LAMBDA : IrDeclarationOriginImpl("SUSPEND_LAMBDA")
|
||||
object FOR_INLINE_STATE_MACHINE_TEMPLATE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE")
|
||||
object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE")
|
||||
object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE_VIEW")
|
||||
object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_MOVED_PROPERTY_BACKING_FIELD")
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.MemoizedInlineClassReplacements
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.inline.NameGenerator
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
@@ -108,7 +109,7 @@ class JvmBackendContext(
|
||||
val suspendLambdaToOriginalFunctionMap = mutableMapOf<IrFunctionReference, IrFunction>()
|
||||
val continuationClassBuilders = mutableMapOf<IrSimpleFunction, ClassBuilder>()
|
||||
val suspendFunctionOriginalToView = mutableMapOf<IrFunction, IrFunction>()
|
||||
val suspendFunctionViewToOriginal = mutableMapOf<IrFunction, IrFunction>()
|
||||
val suspendFunctionOriginalToStub = mutableMapOf<IrFunction, IrFunction>()
|
||||
val suspendTailCallsWithUnitReplacement = mutableSetOf<IrAttributeContainer>()
|
||||
val fakeContinuation: IrExpression = createFakeContinuation(this)
|
||||
|
||||
@@ -117,8 +118,13 @@ class JvmBackendContext(
|
||||
val inlineClassReplacements = MemoizedInlineClassReplacements()
|
||||
|
||||
internal fun recordSuspendFunctionView(function: IrFunction, view: IrFunction) {
|
||||
suspendFunctionOriginalToView[function] = view
|
||||
suspendFunctionViewToOriginal[view] = function
|
||||
val attribute = function.suspendFunctionOriginal()
|
||||
suspendFunctionOriginalToStub.remove(attribute)
|
||||
suspendFunctionOriginalToView[attribute] = view
|
||||
}
|
||||
|
||||
internal fun recordSuspendFunctionViewStub(function: IrFunction, stub: IrFunction) {
|
||||
suspendFunctionOriginalToStub[function.suspendFunctionOriginal()] = stub
|
||||
}
|
||||
|
||||
internal fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol =
|
||||
|
||||
+11
-120
@@ -6,36 +6,29 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyValueParametersInsertingContinuationFrom
|
||||
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
||||
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
|
||||
import org.jetbrains.kotlin.backend.common.lower.allOverridden
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionOriginal
|
||||
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.codegen.coroutines.reportSuspensionPointInsideMonitor
|
||||
import org.jetbrains.kotlin.codegen.inline.addFakeContinuationConstructorCallMarker
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFunWithDescriptorForInlining
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.name
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.types.createType
|
||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
||||
@@ -71,8 +64,9 @@ internal fun generateStateMachineForNamedFunction(
|
||||
|| irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION,
|
||||
internalNameForDispatchReceiver = classCodegen.visitor.thisName,
|
||||
putContinuationParameterToLvt = false,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = irFunction.returnType.isUnit() &&
|
||||
irFunction.anyOfOverriddenFunctionsReturnsNonUnit()
|
||||
disableTailCallOptimizationForFunctionReturningUnit = irFunction.suspendFunctionOriginal().let {
|
||||
it.returnType.isUnit() && it.anyOfOverriddenFunctionsReturnsNonUnit()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -134,109 +128,6 @@ internal fun IrFunction.isKnownToBeTailCall(): Boolean =
|
||||
internal fun IrFunction.shouldNotContainSuspendMarkers(): Boolean =
|
||||
isInvokeSuspendOfContinuation() || isKnownToBeTailCall()
|
||||
|
||||
// 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 || origin == JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW ||
|
||||
origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW
|
||||
) return this
|
||||
return context.suspendFunctionOriginalToView[this] ?: suspendFunctionView(context, true)
|
||||
}
|
||||
|
||||
fun IrFunction.suspendFunctionView(context: JvmBackendContext, generateBody: Boolean): IrFunction {
|
||||
require(this.isSuspend && this is IrSimpleFunction)
|
||||
// For SuspendFunction{N}.invoke we need to generate INVOKEINTERFACE Function{N+1}.invoke(...Ljava/lang/Object;)...
|
||||
// instead of INVOKEINTERFACE Function{N+1}.invoke(...Lkotlin/coroutines/Continuation;)...
|
||||
val isInvokeOfNumberedSuspendFunction = (parent as? IrClass)?.defaultType?.isSuspendFunction() == true
|
||||
// And we need to generate this function for callable references
|
||||
val isBridgeInvokeOfCallableReference = origin == IrDeclarationOrigin.BRIDGE &&
|
||||
(parent as? IrClass)?.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
val continuationType = if (isInvokeOfNumberedSuspendFunction || isBridgeInvokeOfCallableReference)
|
||||
context.irBuiltIns.anyNType
|
||||
else
|
||||
context.ir.symbols.continuationClass.createType(false, listOf(makeTypeProjection(returnType, Variance.INVARIANT)))
|
||||
return buildFunWithDescriptorForInlining(descriptor) {
|
||||
updateFrom(this@suspendFunctionView)
|
||||
name = this@suspendFunctionView.name
|
||||
origin = if (origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE)
|
||||
JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW
|
||||
else
|
||||
JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW
|
||||
returnType = context.irBuiltIns.anyNType
|
||||
}.also {
|
||||
it.parent = parent
|
||||
|
||||
it.annotations.addAll(annotations)
|
||||
|
||||
it.copyAttributes(this)
|
||||
it.copyTypeParametersFrom(this)
|
||||
|
||||
// Copy the value parameters and insert the continuation parameter. The continuation parameter
|
||||
// goes before the default argument mask(s) and handler for default argument stubs.
|
||||
// TODO: It would be nice if AddContinuationLowering could insert the continuation argument before default stub generation.
|
||||
// That would avoid the reshuffling both here and in createSuspendFunctionClassViewIfNeeded.
|
||||
var continuationValueParam: IrValueParameter? = null
|
||||
it.copyValueParametersInsertingContinuationFrom(this) {
|
||||
continuationValueParam = it.addValueParameter(SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, continuationType)
|
||||
}
|
||||
|
||||
if (generateBody) {
|
||||
// Add the suspend function view to the map before transforming the body to make sure
|
||||
// that recursive suspend functions do not lead to unbounded recursion at compile time.
|
||||
context.recordSuspendFunctionView(this, it)
|
||||
|
||||
val valueParametersMapping = explicitParameters.zip(it.explicitParameters.filter { it != continuationValueParam }).toMap()
|
||||
it.body = body?.deepCopyWithSymbols(this)
|
||||
it.body?.transformChildrenVoid(object : VariableRemapper(valueParametersMapping) {
|
||||
// Do not cross class boundaries inside functions. Otherwise, callable references will try to access wrong $completion.
|
||||
override fun visitClass(declaration: IrClass): IrStatement = declaration
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression =
|
||||
super.visitCall(expression.createSuspendFunctionCallViewIfNeeded(context, it, callerIsInlineLambda = false))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IrCall.createSuspendFunctionCallViewIfNeeded(
|
||||
context: JvmBackendContext,
|
||||
caller: IrFunction,
|
||||
callerIsInlineLambda: Boolean
|
||||
): 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, superQualifierSymbol = superQualifierSymbol).also {
|
||||
it.copyTypeArgumentsFrom(this)
|
||||
it.dispatchReceiver = dispatchReceiver
|
||||
it.extensionReceiver = extensionReceiver
|
||||
// Locate the caller continuation parameter. The continuation parameter is before default argument mask(s) and handler params.
|
||||
val callerNumberOfMasks = caller.valueParameters.count { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION }
|
||||
val callerContinuationIndex = caller.valueParameters.size - 1 - (if (callerNumberOfMasks != 0) callerNumberOfMasks + 1 else 0)
|
||||
val continuationParameter =
|
||||
when {
|
||||
caller.isInvokeSuspendOfLambda() || caller.isInvokeSuspendOfContinuation() ||
|
||||
caller.isInvokeSuspendForInlineOfLambda() ->
|
||||
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.dispatchReceiverParameter!!.symbol)
|
||||
callerIsInlineLambda -> context.fakeContinuation
|
||||
else -> IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.valueParameters[callerContinuationIndex].symbol)
|
||||
}
|
||||
// If the suspend function view that we are calling has default parameters, we need to make sure to pass the
|
||||
// continuation before the default parameter mask(s) and handler.
|
||||
val numberOfMasks = view.valueParameters.count { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION }
|
||||
val continuationIndex = valueArgumentsCount - (if (numberOfMasks != 0) numberOfMasks + 1 else 0)
|
||||
for (i in 0 until continuationIndex) {
|
||||
it.putValueArgument(i, getValueArgument(i))
|
||||
}
|
||||
it.putValueArgument(continuationIndex, continuationParameter)
|
||||
if (numberOfMasks != 0) {
|
||||
for (i in 0 until numberOfMasks + 1) {
|
||||
it.putValueArgument(valueArgumentsCount + i - 1, getValueArgument(continuationIndex + i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createFakeContinuation(context: JvmBackendContext): IrExpression = IrErrorExpressionImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
|
||||
+26
-18
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER
|
||||
import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin.*
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||
@@ -52,6 +53,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
@@ -166,7 +168,7 @@ class ExpressionCodegen(
|
||||
|
||||
// TODO remove
|
||||
fun gen(expression: IrExpression, type: Type, irType: IrType, data: BlockInfo): StackValue {
|
||||
if (expression === context.fakeContinuation) {
|
||||
if (expression.attributeOwnerId === context.fakeContinuation) {
|
||||
addFakeContinuationMarker(mv)
|
||||
} else {
|
||||
expression.accept(this, data).coerce(type, irType).materialize()
|
||||
@@ -219,10 +221,11 @@ class ExpressionCodegen(
|
||||
}
|
||||
|
||||
private fun generateFakeContinuationConstructorIfNeeded() {
|
||||
if (irFunction.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW) return
|
||||
if (irFunction.origin != FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE) return
|
||||
val continuationClass = classCodegen.irClass.functions.find {
|
||||
it.name.asString() == irFunction.name.asString().removeSuffix(FOR_INLINE_SUFFIX)
|
||||
}?.body?.statements?.get(0) ?: error("could not find continuation for ${irFunction.render()}")
|
||||
it.attributeOwnerId == (irFunction as? IrSimpleFunction)?.attributeOwnerId &&
|
||||
it.name.asString() == irFunction.name.asString().removeSuffix(FOR_INLINE_SUFFIX)
|
||||
}?.body?.statements?.firstIsInstance<IrClass>() ?: error("could not find continuation for ${irFunction.render()}")
|
||||
generateFakeContinuationConstructorCall(
|
||||
mv,
|
||||
classCodegen.visitor,
|
||||
@@ -254,12 +257,10 @@ class ExpressionCodegen(
|
||||
if (notCallableFromJava)
|
||||
return
|
||||
|
||||
// Do not generate non-null checks for suspend function views. When resumed the arguments
|
||||
// Do not generate non-null checks for suspend functions. When resumed the arguments
|
||||
// will be null and the actual values are taken from the continuation.
|
||||
val isSuspendFunctionView = irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW ||
|
||||
irFunction.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW
|
||||
|
||||
if (isSuspendFunctionView)
|
||||
if (irFunction.isSuspend)
|
||||
return
|
||||
|
||||
irFunction.extensionReceiverParameter?.let { generateNonNullAssertion(it) }
|
||||
@@ -361,10 +362,6 @@ class ExpressionCodegen(
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) =
|
||||
visitStatementContainer(expression, data).coerce(expression.type)
|
||||
|
||||
override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue {
|
||||
return visitFunctionAccess(expression.createSuspendFunctionCallViewIfNeeded(context, irFunction, inlinedInto != null), data)
|
||||
}
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
|
||||
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
|
||||
?.invoke(expression, this, data)?.let { return it }
|
||||
@@ -445,7 +442,7 @@ class ExpressionCodegen(
|
||||
// Check return type of non-lowered suspend call, in order to replace the result of the call with Unit,
|
||||
// otherwise, it would seem like the call returns non-unit upon resume.
|
||||
// See box/coroutines/tailCallOptimization/unit tests.
|
||||
if (context.suspendFunctionViewToOriginal[expression.symbol.owner]?.returnType?.isUnit() == true) {
|
||||
if (((expression.symbol.owner as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.returnType?.isUnit() == true) {
|
||||
addReturnsUnitMarker(mv)
|
||||
}
|
||||
|
||||
@@ -486,11 +483,11 @@ class ExpressionCodegen(
|
||||
// Inside $$forInline functions crossinline calls are (usually) not suspension points, otherwise, flow will be pessimized
|
||||
(dispatchReceiver as? IrGetField)?.symbol?.owner?.origin ==
|
||||
LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CROSSINLINE_CAPTURED_VALUE ->
|
||||
irFunction.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||
irFunction.origin != FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||
// The same goes for inline lambdas
|
||||
(dispatchReceiver as? IrGetValue)?.let {
|
||||
it.origin == IrStatementOrigin.VARIABLE_AS_FUNCTION && (it.symbol.owner as? IrValueParameter)?.isNoinline != true
|
||||
} == true -> irFunction.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE
|
||||
} == true -> irFunction.origin != FOR_INLINE_STATE_MACHINE_TEMPLATE
|
||||
// Otherwise, this is normal inline call, ergo not a suspension point
|
||||
else -> false
|
||||
}
|
||||
@@ -636,10 +633,21 @@ class ExpressionCodegen(
|
||||
|
||||
override fun visitReturn(expression: IrReturn, data: BlockInfo): PromisedValue {
|
||||
val returnTarget = expression.returnTargetSymbol.owner
|
||||
val owner =
|
||||
(returnTarget as? IrFunction
|
||||
var owner =
|
||||
returnTarget as? IrFunction
|
||||
?: (returnTarget as? IrReturnableBlock)?.inlineFunctionSymbol?.owner
|
||||
?: error("Unsupported IrReturnTarget: $returnTarget")).getOrCreateSuspendFunctionViewIfNeeded(context)
|
||||
?: error("Unsupported IrReturnTarget: $returnTarget")
|
||||
// $$forInline and $suspendImpl functions share the same attributes as originals, but the name is different
|
||||
// fix the return target. TODO: attributes seem to be overloaded. We rely on their uniqueness across transformations to find views
|
||||
// TODO: but in this case, they are not unique enough.
|
||||
if ((irFunction.origin == FOR_INLINE_STATE_MACHINE_TEMPLATE ||
|
||||
irFunction.origin == SUSPEND_IMPL_STATIC_FUNCTION ||
|
||||
irFunction.origin == FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE) &&
|
||||
owner.isSuspend && irFunction is IrSimpleFunction && owner is IrSimpleFunction &&
|
||||
irFunction.attributeOwnerId == owner.attributeOwnerId
|
||||
) {
|
||||
owner = irFunction
|
||||
}
|
||||
//TODO: should be owner != irFunction
|
||||
val isNonLocalReturn =
|
||||
methodSignatureMapper.mapFunctionName(owner) != methodSignatureMapper.mapFunctionName(irFunction)
|
||||
|
||||
+12
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
@@ -53,10 +53,9 @@ open class FunctionCodegen(
|
||||
}
|
||||
|
||||
private fun doGenerate(): JvmMethodGenericSignature {
|
||||
val functionView = irFunction.getOrCreateSuspendFunctionViewIfNeeded(context)
|
||||
val signature = classCodegen.methodSignatureMapper.mapSignatureWithGeneric(functionView)
|
||||
val signature = classCodegen.methodSignatureMapper.mapSignatureWithGeneric(irFunction)
|
||||
|
||||
val flags = calculateMethodFlags(functionView.isStatic)
|
||||
val flags = calculateMethodFlags(irFunction.isStatic)
|
||||
var methodVisitor = createMethod(flags, signature)
|
||||
|
||||
if (state.generateParametersMetadata && flags.and(Opcodes.ACC_SYNTHETIC) == 0) {
|
||||
@@ -75,14 +74,14 @@ open class FunctionCodegen(
|
||||
)
|
||||
}
|
||||
}.genAnnotations(
|
||||
functionView,
|
||||
irFunction,
|
||||
signature.asmMethod.returnType,
|
||||
irFunction.returnType
|
||||
)
|
||||
// Not generating parameter annotations for default stubs fixes KT-7892, though
|
||||
// this certainly looks like a workaround for a javac bug.
|
||||
if (irFunction !is IrConstructor || !irFunction.parentAsClass.shouldNotGenerateConstructorParameterAnnotations()) {
|
||||
generateParameterAnnotations(functionView, methodVisitor, signature, classCodegen, context)
|
||||
generateParameterAnnotations(irFunction, methodVisitor, signature, classCodegen, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +106,7 @@ open class FunctionCodegen(
|
||||
)
|
||||
else -> methodVisitor
|
||||
}
|
||||
ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, inlinedInto).generate()
|
||||
ExpressionCodegen(irFunction, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, inlinedInto).generate()
|
||||
methodVisitor.visitMaxs(-1, -1)
|
||||
if (irFunction.hasContinuation()) {
|
||||
context.continuationClassBuilders[continuationClass().attributeOwnerId].sure {
|
||||
@@ -151,7 +150,8 @@ open class FunctionCodegen(
|
||||
// $$forInline functions never have a continuation
|
||||
origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE
|
||||
|
||||
private fun continuationClass() = irFunction.body!!.statements.firstIsInstance<IrClass>()
|
||||
private fun continuationClass() =
|
||||
irFunction.body!!.statements.firstIsInstance<IrClass>()
|
||||
|
||||
private fun IrFunction.getVisibilityForDefaultArgumentStub(): Int =
|
||||
when (visibility) {
|
||||
@@ -260,17 +260,16 @@ open class FunctionCodegen(
|
||||
|
||||
private fun createFrameMapWithReceivers(): IrFrameMap {
|
||||
val frameMap = IrFrameMap()
|
||||
val functionView = irFunction.getOrCreateSuspendFunctionViewIfNeeded(context)
|
||||
|
||||
if (irFunction is IrConstructor) {
|
||||
frameMap.enterDispatchReceiver(irFunction.constructedClass.thisReceiver!!)
|
||||
} else if (functionView.dispatchReceiverParameter != null) {
|
||||
frameMap.enterDispatchReceiver(functionView.dispatchReceiverParameter!!)
|
||||
} else if (irFunction.dispatchReceiverParameter != null) {
|
||||
frameMap.enterDispatchReceiver(irFunction.dispatchReceiverParameter!!)
|
||||
}
|
||||
functionView.extensionReceiverParameter?.let {
|
||||
irFunction.extensionReceiverParameter?.let {
|
||||
frameMap.enter(it, classCodegen.typeMapper.mapType(it))
|
||||
}
|
||||
for (parameter in functionView.valueParameters) {
|
||||
for (parameter in irFunction.valueParameters) {
|
||||
frameMap.enter(parameter, classCodegen.typeMapper.mapType(parameter.type))
|
||||
}
|
||||
|
||||
|
||||
+6
-8
@@ -1,24 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.jvm.codegen
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.suspendFunctionViewOrStub
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isLambda
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
@@ -251,7 +249,7 @@ class IrExpressionLambdaImpl(
|
||||
capturedParamDesc(param.name.asString(), typeMapper.mapType(param.type))
|
||||
}
|
||||
|
||||
private val loweredMethod = methodSignatureMapper.mapAsmMethod(function.getOrCreateSuspendFunctionViewIfNeeded(context))
|
||||
private val loweredMethod = methodSignatureMapper.mapAsmMethod(function.suspendFunctionViewOrStub(context))
|
||||
|
||||
val capturedParamsInDesc: List<Type> = if (isBoundCallableReference) {
|
||||
loweredMethod.argumentTypes.take(1)
|
||||
@@ -274,7 +272,7 @@ class IrExpressionLambdaImpl(
|
||||
override val hasDispatchReceiver: Boolean = false
|
||||
|
||||
override fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor {
|
||||
return function.getOrCreateSuspendFunctionViewIfNeeded(context).descriptor
|
||||
return function.suspendFunctionViewOrStub(context).descriptor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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 com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.getOrCreateSuspendFunctionViewIfNeeded
|
||||
import org.jetbrains.kotlin.codegen.BaseExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
@@ -86,7 +87,7 @@ class IrSourceCompilerForInline(
|
||||
function.parentAsClass.functions.find {
|
||||
it.name.asString() == function.name.asString() + FOR_INLINE_SUFFIX &&
|
||||
it.attributeOwnerId == (function as? IrAttributeContainer)?.attributeOwnerId
|
||||
} ?: function
|
||||
} ?: function.getOrCreateSuspendFunctionViewIfNeeded(classCodegen.context)
|
||||
else function
|
||||
val functionCodegen = object : FunctionCodegen(forInlineFunction, classCodegen, codegen.takeIf { isLambda }) {
|
||||
override fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor {
|
||||
|
||||
+3
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.*
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
|
||||
@@ -208,13 +209,6 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
|
||||
}
|
||||
}
|
||||
|
||||
if (function.isSuspend && function.origin != JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW &&
|
||||
function.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW &&
|
||||
(function.parent as? IrClass)?.origin != JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
) {
|
||||
return mapSignature(function.suspendFunctionView(context, false), skipGenericSignature)
|
||||
}
|
||||
|
||||
val sw = if (skipGenericSignature) JvmSignatureWriter() else BothSignatureWriter(BothSignatureWriter.Mode.METHOD)
|
||||
|
||||
typeMapper.writeFormalTypeParameters(function.typeParameters, sw)
|
||||
@@ -300,7 +294,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
|
||||
}
|
||||
|
||||
fun mapToCallableMethod(caller: IrFunction, expression: IrFunctionAccessExpression): IrCallableMethod {
|
||||
val callee = expression.symbol.owner.getOrCreateSuspendFunctionViewIfNeeded(context)
|
||||
val callee = expression.symbol.owner
|
||||
val calleeParent = callee.parentAsClass
|
||||
val owner = typeMapper.mapClass(calleeParent)
|
||||
|
||||
|
||||
+201
-38
@@ -14,7 +14,9 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.defaultValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrBlock
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInvokeSuspendForInlineOfLambda
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInvokeSuspendOfContinuation
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInvokeSuspendOfLambda
|
||||
import org.jetbrains.kotlin.backend.jvm.localDeclarationsPhase
|
||||
import org.jetbrains.kotlin.codegen.coroutines.*
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
|
||||
@@ -28,9 +30,7 @@ import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
@@ -47,19 +47,45 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
internal val addContinuationPhase = makeIrFilePhase(
|
||||
::AddContinuationLowering,
|
||||
"AddContinuation",
|
||||
"Add continuation classes to suspend functions and transform suspend lambdas into continuations",
|
||||
"Add continuation classes and parameters to suspend functions and transform suspend lambdas into continuations",
|
||||
prerequisite = setOf(localDeclarationsPhase, tailCallOptimizationPhase)
|
||||
)
|
||||
|
||||
private class AddContinuationLowering(private val context: JvmBackendContext) : FileLoweringPass {
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
val (suspendLambdas, inlineLambdas) = markSuspendLambdas(irFile)
|
||||
transformSuspendFunctions(irFile, (suspendLambdas.map { it.function } + inlineLambdas).toSet())
|
||||
transformReferencesToSuspendLambdas(irFile, suspendLambdas)
|
||||
val suspendLambdas = findSuspendAndInlineLambdas(irFile)
|
||||
addContinuationObjectAndContinuationParameterToSuspendFunctions(irFile)
|
||||
transformSuspendLambdasIntoContinuations(irFile, suspendLambdas)
|
||||
addContinuationParameterToSuspendCallsAndUpdateNonLocalReturns(irFile)
|
||||
}
|
||||
|
||||
private fun transformReferencesToSuspendLambdas(irFile: IrFile, suspendLambdas: List<SuspendLambdaInfo>) {
|
||||
private fun addContinuationParameterToSuspendCallsAndUpdateNonLocalReturns(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
val functionStack = mutableListOf<IrFunction>()
|
||||
|
||||
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||
functionStack.push(declaration)
|
||||
return super.visitFunction(declaration).also { functionStack.pop() }
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
// This is a property, no need to add continuation parameter, since this cannot be suspend call
|
||||
if (functionStack.isEmpty()) return super.visitCall(expression)
|
||||
val caller = functionStack.peek()!!
|
||||
return (super.visitCall(expression) as IrCall)
|
||||
.createSuspendFunctionCallViewIfNeeded(context, caller)
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
val ret = super.visitReturn(expression) as IrReturn
|
||||
val irFunction = expression.returnTargetSymbol.owner as? IrFunction ?: return ret
|
||||
val targetViewOrStub = irFunction.suspendFunctionViewOrStub(context)
|
||||
return IrReturnImpl(ret.startOffset, ret.endOffset, ret.type, targetViewOrStub.symbol, ret.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun transformSuspendLambdasIntoContinuations(irFile: IrFile, suspendLambdas: List<SuspendLambdaInfo>) {
|
||||
for (lambda in suspendLambdas) {
|
||||
(lambda.function.parent as IrDeclarationContainer).declarations.remove(lambda.function)
|
||||
}
|
||||
@@ -508,9 +534,13 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, backendContext
|
||||
)
|
||||
}
|
||||
for ((i, parameter) in irFunction.valueParameters.withIndex()) {
|
||||
for ((i, parameter) in irFunction.valueParameters.dropLast(1).withIndex()) {
|
||||
it.putValueArgument(i, parameter.type.defaultValue(UNDEFINED_OFFSET, UNDEFINED_OFFSET, backendContext))
|
||||
}
|
||||
it.putValueArgument(
|
||||
irFunction.valueParameters.size - 1,
|
||||
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.dispatchReceiverParameter!!.symbol)
|
||||
)
|
||||
if (isStaticSuspendImpl) {
|
||||
it.putValueArgument(0, capturedThisValue)
|
||||
}
|
||||
@@ -529,6 +559,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
copyMetadata = false
|
||||
)
|
||||
static.body = irFunction.moveBodyTo(static)
|
||||
static.copyAttributes(irFunction)
|
||||
// Rewrite the body of the original suspend method to forward to the new static method.
|
||||
irFunction.body = context.createIrBuilder(irFunction.symbol).irBlockBody {
|
||||
+irReturn(irCall(static).also {
|
||||
@@ -547,11 +578,10 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
}
|
||||
})
|
||||
}
|
||||
static.copyAttributes(irFunction)
|
||||
return static
|
||||
}
|
||||
|
||||
private fun transformSuspendFunctions(irFile: IrFile, suspendAndInlineLambdas: Set<IrFunction>) {
|
||||
private fun addContinuationObjectAndContinuationParameterToSuspendFunctions(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
private val functionsStack = arrayListOf<IrFunction>()
|
||||
private val suspendFunctionsCapturingCrossinline = mutableSetOf<IrFunction>()
|
||||
@@ -573,39 +603,45 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
functionsStack.pop()
|
||||
if (skip(function)) return function
|
||||
|
||||
function as IrSimpleFunction
|
||||
// TODO: This does not work for DEFAULT_IMPLS
|
||||
val view =
|
||||
(if (function.body == null) function.suspendFunctionStub(context, false)
|
||||
else function.getOrCreateSuspendFunctionViewIfNeeded(context)) as IrSimpleFunction
|
||||
|
||||
if (withoutContinuationClass(function)) return view
|
||||
|
||||
if (function in suspendFunctionsCapturingCrossinline || function.isInline) {
|
||||
val newFunction = buildFunWithDescriptorForInlining(function.descriptor) {
|
||||
name = Name.identifier(function.name.asString() + FOR_INLINE_SUFFIX)
|
||||
returnType = function.returnType
|
||||
modality = function.modality
|
||||
isSuspend = function.isSuspend
|
||||
isInline = function.isInline
|
||||
val newFunction = buildFunWithDescriptorForInlining(view.descriptor) {
|
||||
name = Name.identifier(view.name.asString() + FOR_INLINE_SUFFIX)
|
||||
returnType = view.returnType
|
||||
modality = view.modality
|
||||
isSuspend = view.isSuspend
|
||||
isInline = view.isInline
|
||||
origin =
|
||||
if (function.isInline) JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE
|
||||
if (view.isInline) JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE
|
||||
else JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||
}.apply {
|
||||
copyTypeParameters(function.typeParameters)
|
||||
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this)
|
||||
extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(this)
|
||||
function.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
|
||||
body = function.copyBodyTo(this)
|
||||
copyAttributes(function)
|
||||
copyTypeParameters(view.typeParameters)
|
||||
dispatchReceiverParameter = view.dispatchReceiverParameter?.copyTo(this)
|
||||
extensionReceiverParameter = view.extensionReceiverParameter?.copyTo(this)
|
||||
view.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
|
||||
body = view.copyBodyTo(this)
|
||||
copyAttributes(view)
|
||||
}
|
||||
registerNewFunction(newFunction)
|
||||
}
|
||||
|
||||
val newFunction = if (function.isOverridable) {
|
||||
val newFunction = if ((function as IrSimpleFunction).isOverridable) {
|
||||
// Create static method for the suspend state machine method so that reentering the method
|
||||
// does not lead to virtual dispatch to the wrong method.
|
||||
registerNewFunction(function)
|
||||
createStaticSuspendImpl(function)
|
||||
} else function
|
||||
registerNewFunction(view)
|
||||
createStaticSuspendImpl(view)
|
||||
} else view
|
||||
|
||||
newFunction.body = context.createIrBuilder(newFunction.symbol).irBlockBody {
|
||||
+generateContinuationClassForNamedFunction(
|
||||
newFunction,
|
||||
function.dispatchReceiverParameter,
|
||||
view.dispatchReceiverParameter,
|
||||
declaration as IrAttributeContainer
|
||||
)
|
||||
for (statement in newFunction.body!!.statements) {
|
||||
@@ -615,11 +651,16 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
return newFunction
|
||||
}
|
||||
|
||||
private fun skip(function: IrFunction) =
|
||||
!function.isSuspend || function in suspendAndInlineLambdas || function.body == null ||
|
||||
function.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE ||
|
||||
// TODO: Merge with `knownToBeTailCall`
|
||||
private fun withoutContinuationClass(function: IrFunction): Boolean =
|
||||
function.body == null ||
|
||||
function.parentAsClass.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL ||
|
||||
function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
|
||||
function.parentAsClass.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
function.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE
|
||||
|
||||
private fun skip(function: IrFunction) =
|
||||
!function.isSuspend ||
|
||||
function.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
|
||||
private fun registerNewFunction(function: IrFunction) {
|
||||
functionsToAdd.peek()!!.add(function)
|
||||
@@ -638,7 +679,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
})
|
||||
}
|
||||
|
||||
private fun markSuspendLambdas(irElement: IrElement): Pair<List<SuspendLambdaInfo>, List<IrFunction>> {
|
||||
private fun findSuspendAndInlineLambdas(irElement: IrElement): List<SuspendLambdaInfo> {
|
||||
val suspendLambdas = arrayListOf<SuspendLambdaInfo>()
|
||||
val capturesCrossinline = mutableSetOf<IrCallableReference>()
|
||||
val inlineReferences = mutableSetOf<IrCallableReference>()
|
||||
@@ -678,7 +719,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
}
|
||||
}
|
||||
})
|
||||
return suspendLambdas to inlineReferences.map { it.symbol.owner as IrFunction }
|
||||
return suspendLambdas
|
||||
}
|
||||
|
||||
private class SuspendLambdaInfo(
|
||||
@@ -689,4 +730,126 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
) {
|
||||
lateinit var constructor: IrConstructor
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
return context.suspendFunctionOriginalToView[suspendFunctionOriginal()] ?: suspendFunctionView(context)
|
||||
}
|
||||
|
||||
private fun IrFunction.getOrCreateSuspendFunctionStub(context: JvmBackendContext): IrFunction {
|
||||
if (!isSuspend) return this
|
||||
return context.suspendFunctionOriginalToStub[suspendFunctionOriginal()] ?: suspendFunctionStub(context)
|
||||
}
|
||||
|
||||
internal fun IrFunction.suspendFunctionOriginal(): IrFunction {
|
||||
require(isSuspend && this is IrSimpleFunction)
|
||||
return attributeOwnerId as IrFunction
|
||||
}
|
||||
|
||||
private fun IrFunction.suspendFunctionStub(context: JvmBackendContext): IrFunction {
|
||||
require(this.isSuspend && this is IrSimpleFunction)
|
||||
return buildFunWithDescriptorForInlining(descriptor) {
|
||||
updateFrom(this@suspendFunctionStub)
|
||||
name = this@suspendFunctionStub.name
|
||||
origin = this@suspendFunctionStub.origin
|
||||
returnType = context.irBuiltIns.anyNType
|
||||
}.also { function ->
|
||||
function.parent = parent
|
||||
|
||||
function.annotations.addAll(annotations)
|
||||
function.metadata = metadata
|
||||
|
||||
function.copyAttributes(this)
|
||||
function.copyTypeParametersFrom(this)
|
||||
|
||||
if (origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE) {
|
||||
function.overriddenSymbols
|
||||
.addAll(overriddenSymbols.map { it.owner.suspendFunctionViewOrStub(context).symbol as IrSimpleFunctionSymbol })
|
||||
}
|
||||
|
||||
// Copy the value parameters and insert the continuation parameter. The continuation parameter
|
||||
// goes before the default argument mask(s) and handler for default argument stubs.
|
||||
// TODO: It would be nice if AddContinuationLowering could insert the continuation argument before default stub generation.
|
||||
// That would avoid the reshuffling both here and in createSuspendFunctionCallViewIfNeeded.
|
||||
function.copyValueParametersInsertingContinuationFrom(this) {
|
||||
function.addValueParameter(SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, continuationType(context))
|
||||
}
|
||||
|
||||
context.recordSuspendFunctionViewStub(this, function)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.continuationType(context: JvmBackendContext): IrType {
|
||||
// For SuspendFunction{N}.invoke we need to generate INVOKEINTERFACE Function{N+1}.invoke(...Ljava/lang/Object;)...
|
||||
// instead of INVOKEINTERFACE Function{N+1}.invoke(...Lkotlin/coroutines/Continuation;)...
|
||||
val isInvokeOfNumberedSuspendFunction = (parent as? IrClass)?.defaultType?.isSuspendFunction() == true
|
||||
val isInvokeOfNumberedFunction = (parent as? IrClass)?.fqNameWhenAvailable?.asString()?.let {
|
||||
it.startsWith("kotlin.jvm.functions.Function") && it.removePrefix("kotlin.jvm.functions.Function").all { c -> c.isDigit() }
|
||||
} == true
|
||||
return if (isInvokeOfNumberedSuspendFunction || isInvokeOfNumberedFunction)
|
||||
context.irBuiltIns.anyNType
|
||||
else
|
||||
context.ir.symbols.continuationClass.typeWith(returnType)
|
||||
}
|
||||
|
||||
private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFunction {
|
||||
require(isSuspend && this is IrSimpleFunction)
|
||||
return getOrCreateSuspendFunctionStub(context).also { function ->
|
||||
context.recordSuspendFunctionView(this, function)
|
||||
|
||||
val continuationParameter =
|
||||
function.valueParameters.find { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION }
|
||||
?.let { function.valueParameters[it.index - 1] } ?: function.valueParameters.last()
|
||||
|
||||
function.body =
|
||||
moveBodyTo(function, explicitParameters.zip(function.explicitParameters.filter { it != continuationParameter }).toMap())
|
||||
}
|
||||
}
|
||||
|
||||
fun IrFunction.suspendFunctionViewOrStub(context: JvmBackendContext): IrFunction {
|
||||
if (!isSuspend) return this
|
||||
return context.suspendFunctionOriginalToView[suspendFunctionOriginal()] ?: getOrCreateSuspendFunctionStub(context)
|
||||
}
|
||||
|
||||
private fun IrCall.createSuspendFunctionCallViewIfNeeded(context: JvmBackendContext, caller: IrFunction): IrCall {
|
||||
// Calls inside continuation are already generated with continuation parameter as well as calls to suspendImpls
|
||||
if (!isSuspend || caller.isInvokeSuspendOfContinuation()
|
||||
|| symbol.owner.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION
|
||||
|| (symbol.owner.valueParameters.lastOrNull()?.name?.asString() == SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME)
|
||||
) return this
|
||||
val view = (symbol.owner as IrSimpleFunction).suspendFunctionViewOrStub(context)
|
||||
if (view == symbol.owner) return this
|
||||
|
||||
return IrCallImpl(startOffset, endOffset, view.returnType, view.symbol, superQualifierSymbol = superQualifierSymbol).also {
|
||||
it.copyTypeArgumentsFrom(this)
|
||||
it.copyAttributes(this)
|
||||
it.dispatchReceiver = dispatchReceiver
|
||||
it.extensionReceiver = extensionReceiver
|
||||
val callerNumberOfMasks = caller.valueParameters.count { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION }
|
||||
val callerContinuationIndex = caller.valueParameters.size - 1 - (if (callerNumberOfMasks != 0) callerNumberOfMasks + 1 else 0)
|
||||
val continuationParameter =
|
||||
when {
|
||||
caller.isInvokeSuspendOfLambda() || caller.isInvokeSuspendForInlineOfLambda() ->
|
||||
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.dispatchReceiverParameter!!.symbol)
|
||||
// At this point the only LOCAL_FUNCTION_FOR_LAMBDAs are inline and crossinline lambdas
|
||||
caller.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA -> context.fakeContinuation
|
||||
else -> IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.valueParameters[callerContinuationIndex].symbol)
|
||||
}
|
||||
// If the suspend function view that we are calling has default parameters, we need to make sure to pass the
|
||||
// continuation before the default parameter mask(s) and handler.
|
||||
val numberOfMasks = view.valueParameters.count { it.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION }
|
||||
val continuationIndex = valueArgumentsCount - (if (numberOfMasks != 0) numberOfMasks + 1 else 0)
|
||||
for (i in 0 until continuationIndex) {
|
||||
it.putValueArgument(i, getValueArgument(i))
|
||||
}
|
||||
it.putValueArgument(continuationIndex, continuationParameter)
|
||||
if (numberOfMasks != 0) {
|
||||
for (i in 0 until numberOfMasks + 1) {
|
||||
it.putValueArgument(valueArgumentsCount + i - 1, getValueArgument(continuationIndex + i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
@@ -37,10 +37,13 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -424,7 +427,11 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass
|
||||
IrValueParameterSymbolImpl(descriptor),
|
||||
name,
|
||||
index,
|
||||
type.eraseTypeParameters(),
|
||||
// SuspendFunction{N} is Function{N+1} at runtime, thus, when we generate a bridge for suspend callable reference,
|
||||
// we need to replace type of its continuation parameter with Any?
|
||||
if (target.isSuspend && type.eraseTypeParameters().getClass()
|
||||
?.fqNameWhenAvailable == DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME_RELEASE
|
||||
) context.irBuiltIns.anyNType else type.eraseTypeParameters(),
|
||||
varargElementType?.eraseTypeParameters(),
|
||||
isCrossinline,
|
||||
isNoinline
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
@@ -88,7 +88,7 @@ private class FunctionNVarargBridgeLowering(val context: JvmBackendContext) :
|
||||
|
||||
// Add vararg invoke bridge
|
||||
val invokeFunction = declaration.functions.single {
|
||||
it.name.asString() == "invoke" && it.valueParameters.size == superType.arguments.size - 1
|
||||
it.name.asString() == "invoke" && it.valueParameters.size == superType.arguments.size - if (it.isSuspend) 0 else 1
|
||||
}
|
||||
invokeFunction.overriddenSymbols.clear()
|
||||
declaration.addBridge(invokeFunction, functionNInvokeFun.owner)
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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-2020 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.util
|
||||
@@ -179,6 +168,7 @@ open class DeepCopyIrTreeWithSymbols(
|
||||
declaration.overriddenSymbols.mapTo(overriddenSymbols) {
|
||||
symbolRemapper.getReferencedFunction(it) as IrSimpleFunctionSymbol
|
||||
}
|
||||
copyAttributes(declaration)
|
||||
transformFunctionChildren(declaration)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
+3
-1
@@ -6,6 +6,7 @@ final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$1 {
|
||||
field L$2: java.lang.Object
|
||||
field L$3: java.lang.Object
|
||||
field L$4: java.lang.Object
|
||||
field L$5: java.lang.Object
|
||||
field label: int
|
||||
@org.jetbrains.annotations.NotNull field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1
|
||||
@@ -121,6 +122,7 @@ final class CrossinlineKt$filter$$inlined$source$1$1 {
|
||||
field L$2: java.lang.Object
|
||||
field L$3: java.lang.Object
|
||||
field L$4: java.lang.Object
|
||||
field L$5: java.lang.Object
|
||||
field label: int
|
||||
@org.jetbrains.annotations.NotNull field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1
|
||||
@@ -146,6 +148,7 @@ final class CrossinlineKt$filter$$inlined$source$2$1 {
|
||||
field L$2: java.lang.Object
|
||||
field L$3: java.lang.Object
|
||||
field L$4: java.lang.Object
|
||||
field L$5: java.lang.Object
|
||||
field label: int
|
||||
@org.jetbrains.annotations.NotNull field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$filter$$inlined$source$2
|
||||
@@ -310,7 +313,6 @@ final class CrossinlineKt$range$$inlined$source$1$1 {
|
||||
field L$1: java.lang.Object
|
||||
field L$2: java.lang.Object
|
||||
field L$3: java.lang.Object
|
||||
field L$4: java.lang.Object
|
||||
field label: int
|
||||
@org.jetbrains.annotations.NotNull field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$range$$inlined$source$1
|
||||
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
|
||||
// FILE: test.kt
|
||||
// COMMON_COROUTINES_TEST
|
||||
// WITH_RUNTIME
|
||||
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
|
||||
// FILE: test.kt
|
||||
// COMMON_COROUTINES_TEST
|
||||
// WITH_RUNTIME
|
||||
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
|
||||
// FILE: test.kt
|
||||
// COMMON_COROUTINES_TEST
|
||||
// WITH_RUNTIME
|
||||
|
||||
Reference in New Issue
Block a user