JVM_IR: move SuspendLambdaLowering next to FunctionReferenceLowering

#KT-42253 Fixed
 #KT-39434 Fixed
This commit is contained in:
pyos
2020-09-25 16:12:17 +02:00
committed by Alexander Udalov
parent 44e0bfe90b
commit 05c856f1f7
5 changed files with 145 additions and 217 deletions
@@ -69,8 +69,8 @@ private fun IrBody.move(
override fun visitBlock(expression: IrBlock): IrExpression { override fun visitBlock(expression: IrBlock): IrExpression {
// Might be an inline lambda argument; if the function has already been moved out, visit it explicitly. // Might be an inline lambda argument; if the function has already been moved out, visit it explicitly.
if (expression.origin == IrStatementOrigin.LAMBDA || expression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION) if (expression.origin == IrStatementOrigin.LAMBDA || expression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION)
if (expression.statements[0] !is IrFunction && expression.statements[1] is IrFunctionReference) if (expression.statements.lastOrNull() is IrFunctionReference && expression.statements.none { it is IrFunction })
(expression.statements[1] as IrFunctionReference).symbol.owner.transformChildrenVoid() (expression.statements.last() as IrFunctionReference).symbol.owner.transformChildrenVoid()
return super.visitBlock(expression) return super.visitBlock(expression)
} }
@@ -113,6 +113,7 @@ internal val localDeclarationsPhase = makeIrFilePhase(
object : VisibilityPolicy { object : VisibilityPolicy {
override fun forClass(declaration: IrClass, inInlineFunctionScope: Boolean): DescriptorVisibility = override fun forClass(declaration: IrClass, inInlineFunctionScope: Boolean): DescriptorVisibility =
if (declaration.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || if (declaration.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL ||
declaration.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA ||
declaration.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL || declaration.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL ||
declaration.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE declaration.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE
) { ) {
@@ -290,6 +291,7 @@ private val jvmFilePhases = listOf(
moveOrCopyCompanionObjectFieldsPhase, moveOrCopyCompanionObjectFieldsPhase,
inlineCallableReferenceToLambdaPhase, inlineCallableReferenceToLambdaPhase,
functionReferencePhase, functionReferencePhase,
suspendLambdaPhase,
propertyReferencePhase, propertyReferencePhase,
constPhase, constPhase,
propertiesPhase, propertiesPhase,
@@ -336,7 +338,6 @@ private val jvmFilePhases = listOf(
interfaceObjectCallsPhase, interfaceObjectCallsPhase,
tailCallOptimizationPhase, tailCallOptimizationPhase,
suspendLambdaPhase,
addContinuationPhase, addContinuationPhase,
innerClassesPhase, innerClassesPhase,
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.codegen.coroutines.*
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
@@ -36,15 +36,14 @@ import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
@@ -52,7 +51,7 @@ import org.jetbrains.org.objectweb.asm.Type
internal val suspendLambdaPhase = makeIrFilePhase( internal val suspendLambdaPhase = makeIrFilePhase(
::SuspendLambdaLowering, ::SuspendLambdaLowering,
"SuspendLambda", "SuspendLambda",
"Transform suspend lambda into continuation classes" "Transform suspend lambdas into continuation classes"
) )
internal val addContinuationPhase = makeIrFilePhase( internal val addContinuationPhase = makeIrFilePhase(
@@ -63,24 +62,10 @@ internal val addContinuationPhase = makeIrFilePhase(
) )
private abstract class SuspendLoweringBase(protected val context: JvmBackendContext) : FileLoweringPass { private abstract class SuspendLoweringBase(protected val context: JvmBackendContext) : FileLoweringPass {
protected fun IrClass.createContinuationClassFor(
parent: IrDeclarationParent,
newOrigin: IrDeclarationOrigin,
newVisibility: DescriptorVisibility
): IrClass = context.irFactory.buildClass {
name = Name.special("<Continuation>")
origin = newOrigin
visibility = newVisibility
}.also { irClass ->
irClass.createImplicitParameterDeclarationWithWrappedDescriptor()
irClass.superTypes += defaultType
irClass.parent = parent
}
protected fun IrClass.addFunctionOverride(function: IrSimpleFunction): IrSimpleFunction = protected fun IrClass.addFunctionOverride(function: IrSimpleFunction): IrSimpleFunction =
addFunction(function.name.asString(), function.returnType).apply { addFunction(function.name.asString(), function.returnType).apply {
overriddenSymbols += function.symbol overriddenSymbols += function.symbol
valueParameters += function.valueParameters.map { it.copyTo(this) } valueParameters = function.valueParameters.map { it.copyTo(this) }
} }
protected fun IrClass.addFunctionOverride( protected fun IrClass.addFunctionOverride(
@@ -105,153 +90,118 @@ private abstract class SuspendLoweringBase(protected val context: JvmBackendCont
context.ir.symbols.continuationClass.typeWith(returnType).makeNullable() context.ir.symbols.continuationClass.typeWith(returnType).makeNullable()
} }
private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLoweringBase(context) { private fun IrFunction.capturesCrossinline(): Boolean {
private class SuspendLambdaInfo(val reference: IrFunctionReference, val isRestricted: Boolean) { var result = false
val function = reference.symbol.owner accept(object : IrElementVisitorVoid {
val arity = (reference.type as IrSimpleType).arguments.size - 1 override fun visitElement(element: IrElement) {
val capturesCrossinline = function.valueParameters.any { reference.getValueArgument(it.index).isReadOfCrossinline() } if (!result) element.acceptChildren(this, null)
lateinit var constructor: IrConstructor
}
override fun lower(irFile: IrFile) {
val inlineReferences = mutableSetOf<IrCallableReference<*>>()
val suspendLambdas = mutableMapOf<IrFunctionReference, SuspendLambdaInfo>()
irFile.acceptChildren(object : IrInlineReferenceLocator(context) {
override fun visitInlineReference(argument: IrCallableReference<*>) {
inlineReferences.add(argument)
}
override fun visitFunctionReference(expression: IrFunctionReference, data: IrDeclaration?) {
expression.acceptChildren(this, data)
if (expression.isSuspend && expression.shouldBeTreatedAsSuspendLambda() && expression !in inlineReferences) {
val isRestricted = expression.symbol.owner.extensionReceiverParameter?.type?.classOrNull?.owner?.annotations?.any {
it.type.classOrNull?.signature == IdSignature.PublicSignature("kotlin.coroutines", "RestrictsSuspension", null, 0)
} == true
suspendLambdas[expression] = SuspendLambdaInfo(expression, isRestricted)
}
}
private fun IrFunctionReference.shouldBeTreatedAsSuspendLambda() =
origin == IrStatementOrigin.LAMBDA ||
origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE ||
origin == IrStatementOrigin.SUSPEND_CONVERSION
}, null)
for (lambda in suspendLambdas.values) {
(lambda.function.parent as? IrDeclarationContainer)?.declarations?.remove(lambda.function)
} }
override fun visitFunction(declaration: IrFunction) {
functions.add(declaration)
super.visitFunction(declaration)
functions.remove(declaration)
}
override fun visitGetValue(expression: IrGetValue) {
result = result || (expression.isReadOfCrossinline() && expression.symbol.owner.parent !in functions)
}
private val functions = mutableSetOf<IrFunction>()
}, null)
return result
}
private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLoweringBase(context) {
override fun lower(irFile: IrFile) {
val inlineReferences = IrInlineReferenceLocator.scan(context, irFile)
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() { irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { private fun IrFunctionReference.shouldBeTreatedAsSuspendLambda() =
val info = suspendLambdas[expression] ?: return super.visitFunctionReference(expression) isSuspend && (origin == IrStatementOrigin.LAMBDA || origin == IrStatementOrigin.SUSPEND_CONVERSION)
return context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset).run {
val expressionArguments = expression.getArguments().map { it.second } override fun visitBlock(expression: IrBlock): IrExpression {
irBlock { val reference = expression.statements.lastOrNull() as? IrFunctionReference ?: return super.visitBlock(expression)
+generateContinuationClassForLambda( if (reference.shouldBeTreatedAsSuspendLambda() && reference !in inlineReferences) {
info, assert(expression.statements.size == 2 && expression.statements[0] is IrFunction)
currentDeclarationParent ?: error("No current declaration parent at ${expression.dump()}"), expression.transformChildrenVoid(this)
(currentFunction?.irElement as? IrFunction)?.isInline == true val parent = currentDeclarationParent ?: error("No current declaration parent at ${reference.dump()}")
) return generateAnonymousObjectForLambda(reference, parent)
val constructor = info.constructor }
assert(constructor.valueParameters.size == expressionArguments.size + 1) { return super.visitBlock(expression)
"Inconsistency between callable reference to suspend lambda and the corresponding continuation"
}
+irCall(constructor.symbol).apply {
expressionArguments.forEachIndexed { index, argument ->
putValueArgument(index, argument)
}
// Pass null as completion parameter
putValueArgument(expressionArguments.size, irNull())
}
}
}.also { it.transformChildrenVoid(this) }
} }
}) })
} }
private fun generateContinuationClassForLambda( private fun generateAnonymousObjectForLambda(reference: IrFunctionReference, parent: IrDeclarationParent) =
info: SuspendLambdaInfo, context.createIrBuilder(reference.symbol).irBlock(reference.startOffset, reference.endOffset) {
parent: IrDeclarationParent, assert(reference.getArguments().isEmpty()) { "lambda with bound arguments: ${reference.render()}" }
insideInlineFunction: Boolean val continuation = generateContinuationClassForLambda(reference, parent)
): IrClass { +continuation
val suspendLambda = +irCall(continuation.constructors.single().symbol).apply {
if (info.isRestricted) context.ir.symbols.restrictedSuspendLambdaClass.owner // Pass null as completion parameter
else context.ir.symbols.suspendLambdaClass.owner putValueArgument(0, irNull())
return suspendLambda.createContinuationClassFor( }
parent, }
JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA,
// Since inline functions can be inlined to different package, we should generate lambdas inside these functions private fun generateContinuationClassForLambda(reference: IrFunctionReference, parent: IrDeclarationParent): IrClass =
// as public context.irFactory.buildClass {
if (insideInlineFunction) DescriptorVisibilities.PUBLIC else JavaDescriptorVisibilities.PACKAGE_VISIBILITY name = SpecialNames.NO_NAME_PROVIDED
).apply { origin = JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA
copyAttributes(info.reference) visibility = DescriptorVisibilities.LOCAL
val functionNClass = context.ir.symbols.getJvmFunctionClass(info.arity + 1) }.apply {
superTypes += this.parent = parent
IrSimpleTypeImpl( createImplicitParameterDeclarationWithWrappedDescriptor()
functionNClass, copyAttributes(reference)
hasQuestionMark = false,
arguments = (info.function.explicitParameters.subList(0, info.arity).map { it.type } val function = reference.symbol.owner
+ info.function.continuationType() + context.irBuiltIns.anyNType) val isRestricted = reference.symbol.owner.extensionReceiverParameter?.type?.classOrNull?.owner?.annotations?.any {
.map { makeTypeProjection(it, Variance.INVARIANT) }, it.type.classOrNull?.signature == IdSignature.PublicSignature("kotlin.coroutines", "RestrictsSuspension", null, 0)
annotations = emptyList() } == true
) val suspendLambda =
if (isRestricted) context.ir.symbols.restrictedSuspendLambdaClass.owner
else context.ir.symbols.suspendLambdaClass.owner
val arity = (reference.type as IrSimpleType).arguments.size - 1
val functionNClass = context.ir.symbols.getJvmFunctionClass(arity + 1)
superTypes += suspendLambda.defaultType
superTypes += functionNClass.typeWith(
function.explicitParameters.subList(0, arity).map { it.type }
+ function.continuationType()
+ context.irBuiltIns.anyNType
)
addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType, JavaDescriptorVisibilities.PACKAGE_VISIBILITY) addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType, JavaDescriptorVisibilities.PACKAGE_VISIBILITY)
val receiverField = info.function.extensionReceiverParameter?.let { val parametersFields = function.explicitParameters.map {
assert(info.arity != 0)
// Do not put '$' at the start, to avoid being caught by inlineCodegenUtils.isCapturedFieldName()
addField { addField {
name = Name.identifier("p\$") // Rename `$this` to avoid being caught by inlineCodegenUtils.isCapturedFieldName()
// NB extension receiver can't be crossinline name = if (it.index < 0) Name.identifier("p\$") else it.name
type = it.type
origin = LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE origin = LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE
type = it.type isFinal = false
visibility = DescriptorVisibilities.PRIVATE visibility = if (it.index < 0) DescriptorVisibilities.PRIVATE else JavaDescriptorVisibilities.PACKAGE_VISIBILITY
} }
} }
val constructor = addPrimaryConstructorForLambda(suspendLambda, arity)
val parametersFields = info.function.valueParameters.map {
addField {
name = it.name
type = it.type
origin = if (it.isCrossinline)
LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CROSSINLINE_CAPTURED_VALUE
else
LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE
isFinal = info.reference.getValueArgument(it.index) != null
visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY
}
}
val fieldsForBound = parametersFields.filter { it.isFinal }
val fieldsForUnbound = listOfNotNull(receiverField) + parametersFields.filter { !it.isFinal }
val constructor = addPrimaryConstructorForLambda(info, fieldsForBound, insideInlineFunction)
val invokeToOverride = functionNClass.functions.single { val invokeToOverride = functionNClass.functions.single {
it.owner.valueParameters.size == info.arity + 1 && it.owner.name.asString() == "invoke" it.owner.valueParameters.size == arity + 1 && it.owner.name.asString() == "invoke"
} }
val createToOverride = suspendLambda.symbol.functions.singleOrNull { val createToOverride = suspendLambda.symbol.functions.singleOrNull {
it.owner.valueParameters.size == info.arity + 1 && it.owner.name.asString() == "create" it.owner.valueParameters.size == arity + 1 && it.owner.name.asString() == "create"
} }
val invokeSuspend = addInvokeSuspendForLambda(info.function, parametersFields, receiverField) val invokeSuspend = addInvokeSuspendForLambda(function, parametersFields)
if (info.capturesCrossinline) { if (function.capturesCrossinline()) {
addInvokeSuspendForInlineForLambda(invokeSuspend) addInvokeSuspendForInlineForLambda(invokeSuspend)
} }
if (createToOverride != null) { if (createToOverride != null) {
val create = addCreate(constructor, createToOverride, fieldsForBound, fieldsForUnbound) addInvokeCallingCreate(addCreate(constructor, createToOverride, parametersFields), invokeSuspend, invokeToOverride)
addInvokeCallingCreate(create, invokeSuspend, invokeToOverride)
} else { } else {
addInvokeCallingConstructor(constructor, invokeSuspend, invokeToOverride, fieldsForBound, fieldsForUnbound) addInvokeCallingConstructor(constructor, invokeSuspend, invokeToOverride, parametersFields)
} }
context.suspendLambdaToOriginalFunctionMap[attributeOwnerId as IrFunctionReference] = info.function context.suspendLambdaToOriginalFunctionMap[attributeOwnerId as IrFunctionReference] = function
info.constructor = constructor
} }
}
private fun IrClass.addInvokeSuspendForLambda( private fun IrClass.addInvokeSuspendForLambda(irFunction: IrFunction, fields: List<IrField>): IrSimpleFunction {
irFunction: IrFunction,
fields: List<IrField>,
receiverField: IrField?
): IrSimpleFunction {
val superMethod = context.ir.symbols.suspendLambdaClass.functions.single { val superMethod = context.ir.symbols.suspendLambdaClass.functions.single {
it.owner.name.asString() == INVOKE_SUSPEND_METHOD_NAME && it.owner.valueParameters.size == 1 && it.owner.name.asString() == INVOKE_SUSPEND_METHOD_NAME && it.owner.valueParameters.size == 1 &&
it.owner.valueParameters[0].type.isKotlinResult() it.owner.valueParameters[0].type.isKotlinResult()
@@ -261,7 +211,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
override fun visitGetValue(expression: IrGetValue): IrExpression { override fun visitGetValue(expression: IrGetValue): IrExpression {
val parameter = (expression.symbol.owner as? IrValueParameter)?.takeIf { it.parent == irFunction } val parameter = (expression.symbol.owner as? IrValueParameter)?.takeIf { it.parent == irFunction }
?: return expression ?: return expression
val field = if (parameter.index < 0) receiverField!! else fields[parameter.index] val field = fields[parameter.index + if (irFunction.extensionReceiverParameter != null) 1 else 0]
val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter!!.symbol) val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter!!.symbol)
return IrGetFieldImpl(expression.startOffset, expression.endOffset, field.symbol, field.type, receiver) return IrGetFieldImpl(expression.startOffset, expression.endOffset, field.symbol, field.type, receiver)
} }
@@ -278,7 +228,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
).apply { ).apply {
copyAttributes(invokeSuspend) copyAttributes(invokeSuspend)
generateErrorForInlineBody() generateErrorForInlineBody()
valueParameters += invokeSuspend.valueParameters.map { it.copyTo(this) } valueParameters = invokeSuspend.valueParameters.map { it.copyTo(this) }
} }
} }
@@ -307,35 +257,29 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
constructor: IrFunction, constructor: IrFunction,
invokeSuspend: IrSimpleFunction, invokeSuspend: IrSimpleFunction,
invokeToOverride: IrSimpleFunctionSymbol, invokeToOverride: IrSimpleFunctionSymbol,
fieldsForBound: List<IrField>,
fieldsForUnbound: List<IrField> fieldsForUnbound: List<IrField>
) = addFunctionOverride(invokeToOverride.owner) { function -> ) = addFunctionOverride(invokeToOverride.owner) { function ->
+irReturn(callInvokeSuspend(invokeSuspend, cloneLambda(function, constructor, fieldsForBound, fieldsForUnbound))) +irReturn(callInvokeSuspend(invokeSuspend, cloneLambda(function, constructor, fieldsForUnbound)))
} }
private fun IrClass.addCreate( private fun IrClass.addCreate(
constructor: IrFunction, constructor: IrFunction,
createToOverride: IrSimpleFunctionSymbol, createToOverride: IrSimpleFunctionSymbol,
fieldsForBound: List<IrField>,
fieldsForUnbound: List<IrField> fieldsForUnbound: List<IrField>
) = addFunctionOverride(createToOverride.owner) { function -> ) = addFunctionOverride(createToOverride.owner) { function ->
+irReturn(cloneLambda(function, constructor, fieldsForBound, fieldsForUnbound)) +irReturn(cloneLambda(function, constructor, fieldsForUnbound))
} }
private fun IrBlockBodyBuilder.cloneLambda( private fun IrBlockBodyBuilder.cloneLambda(
scope: IrFunction, scope: IrFunction,
constructor: IrFunction, constructor: IrFunction,
fieldsForBound: List<IrField>,
fieldsForUnbound: List<IrField> fieldsForUnbound: List<IrField>
): IrExpression { ): IrExpression {
val constructorCall = irCall(constructor).also { val constructorCall = irCall(constructor).also {
for (typeParameter in constructor.parentAsClass.typeParameters) { for (typeParameter in constructor.parentAsClass.typeParameters) {
it.putTypeArgument(typeParameter.index, typeParameter.defaultType) it.putTypeArgument(typeParameter.index, typeParameter.defaultType)
} }
for ((index, field) in fieldsForBound.withIndex()) { it.putValueArgument(0, irGet(scope.valueParameters.last()))
it.putValueArgument(index, irGetField(irGet(scope.dispatchReceiverParameter!!), field))
}
it.putValueArgument(fieldsForBound.size, irGet(scope.valueParameters.last()))
} }
if (fieldsForUnbound.isEmpty()) { if (fieldsForUnbound.isEmpty()) {
return constructorCall return constructorCall
@@ -347,44 +291,26 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
return irGet(result) return irGet(result)
} }
private fun IrBlockBodyBuilder.callInvokeSuspend(invokeSuspend: IrSimpleFunction, lambda: IrExpression): IrExpression { private fun IrBlockBodyBuilder.callInvokeSuspend(invokeSuspend: IrSimpleFunction, lambda: IrExpression): IrExpression =
// SingletonReferencesLowering has finished a while ago, so `irUnit()` won't work anymore. irCallOp(invokeSuspend.symbol, invokeSuspend.returnType, lambda, irUnit())
val unitClass = context.irBuiltIns.unitClass
val unitField = this@SuspendLambdaLowering.context.cachedDeclarations.getFieldForObjectInstance(unitClass.owner)
return irCallOp(invokeSuspend.symbol, invokeSuspend.returnType, lambda, irGetField(null, unitField))
}
// Primary constructor accepts parameters equal to function reference arguments + continuation and sets the fields. // Primary constructor accepts parameters equal to function reference arguments + continuation and sets the fields.
private fun IrClass.addPrimaryConstructorForLambda( private fun IrClass.addPrimaryConstructorForLambda(superClass: IrClass, arity: Int): IrConstructor =
info: SuspendLambdaInfo,
fields: List<IrField>,
insideInlineFunction: Boolean
): IrConstructor =
addConstructor { addConstructor {
origin = JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA
isPrimary = true isPrimary = true
returnType = defaultType returnType = defaultType
visibility = if (insideInlineFunction) DescriptorVisibilities.PUBLIC else JavaDescriptorVisibilities.PACKAGE_VISIBILITY visibility = DescriptorVisibilities.LOCAL
}.also { constructor -> }.also { constructor ->
for ((param, arg) in info.reference.getArguments()) {
constructor.addValueParameter(name = param.name.asString(), type = arg.type)
}
val completionParameterSymbol = constructor.addCompletionValueParameter() val completionParameterSymbol = constructor.addCompletionValueParameter()
val superClassConstructor = superClass.constructors.single {
val superClass =
if (info.isRestricted) context.ir.symbols.restrictedSuspendLambdaClass
else context.ir.symbols.suspendLambdaClass
val superClassConstructor = superClass.owner.constructors.single {
it.valueParameters.size == 2 && it.valueParameters[0].type.isInt() && it.valueParameters[1].type.isNullableContinuation() it.valueParameters.size == 2 && it.valueParameters[0].type.isInt() && it.valueParameters[1].type.isNullableContinuation()
} }
constructor.body = context.createIrBuilder(constructor.symbol).irBlockBody { constructor.body = context.createIrBuilder(constructor.symbol).irBlockBody {
+irDelegatingConstructorCall(superClassConstructor).also { +irDelegatingConstructorCall(superClassConstructor).also {
it.putValueArgument(0, irInt(info.arity + 1)) it.putValueArgument(0, irInt(arity + 1))
it.putValueArgument(1, irGet(completionParameterSymbol)) it.putValueArgument(1, irGet(completionParameterSymbol))
} }
for ((index, param) in constructor.valueParameters.dropLast(1).withIndex()) {
+irSetField(irGet(thisReceiver!!), fields[index], irGet(param))
}
} }
} }
} }
@@ -429,41 +355,43 @@ private class AddContinuationLowering(context: JvmBackendContext) : SuspendLower
dispatchReceiverParameter: IrValueParameter?, dispatchReceiverParameter: IrValueParameter?,
attributeContainer: IrAttributeContainer, attributeContainer: IrAttributeContainer,
capturesCrossinline: Boolean capturesCrossinline: Boolean
): IrClass { ): IrClass =
return context.ir.symbols.continuationImplClass.owner context.irFactory.buildClass {
.createContinuationClassFor( name = Name.special("<Continuation>")
irFunction, origin = JvmLoweredDeclarationOrigin.CONTINUATION_CLASS
JvmLoweredDeclarationOrigin.CONTINUATION_CLASS, visibility = if (capturesCrossinline) DescriptorVisibilities.PUBLIC else JavaDescriptorVisibilities.PACKAGE_VISIBILITY
if (capturesCrossinline) DescriptorVisibilities.PUBLIC else JavaDescriptorVisibilities.PACKAGE_VISIBILITY }.apply {
).apply { createImplicitParameterDeclarationWithWrappedDescriptor()
copyTypeParametersFrom(irFunction) superTypes += context.ir.symbols.continuationImplClass.owner.defaultType
val resultField = addField { parent = irFunction
origin = JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD
name = Name.identifier(context.state.languageVersionSettings.dataFieldName()) copyTypeParametersFrom(irFunction)
type = context.irBuiltIns.anyNType val resultField = addField {
visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY origin = JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD
} name = Name.identifier(context.state.languageVersionSettings.dataFieldName())
val capturedThisField = dispatchReceiverParameter?.let { type = context.irBuiltIns.anyNType
addField { visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY
name = Name.identifier("this$0")
type = it.type
origin = InnerClassesSupport.FIELD_FOR_OUTER_THIS
visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY
isFinal = true
}
}
val labelField = addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType, JavaDescriptorVisibilities.PACKAGE_VISIBILITY)
addConstructorForNamedFunction(capturedThisField, capturesCrossinline)
addInvokeSuspendForNamedFunction(
irFunction,
resultField,
labelField,
capturedThisField,
irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION
)
copyAttributes(attributeContainer)
} }
} val capturedThisField = dispatchReceiverParameter?.let {
addField {
name = Name.identifier("this$0")
type = it.type
origin = InnerClassesSupport.FIELD_FOR_OUTER_THIS
visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY
isFinal = true
}
}
val labelField = addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType, JavaDescriptorVisibilities.PACKAGE_VISIBILITY)
addConstructorForNamedFunction(capturedThisField, capturesCrossinline)
addInvokeSuspendForNamedFunction(
irFunction,
resultField,
labelField,
capturedThisField,
irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION
)
copyAttributes(attributeContainer)
}
private fun IrClass.addConstructorForNamedFunction(capturedThisField: IrField?, capturesCrossinline: Boolean): IrConstructor = private fun IrClass.addConstructorForNamedFunction(capturedThisField: IrField?, capturesCrossinline: Boolean): IrConstructor =
addConstructor { addConstructor {
@@ -51,7 +51,7 @@ internal val propertyReferencePhase = makeIrFilePhase(
description = "Construct KProperty instances returned by expressions such as A::x and A()::x", description = "Construct KProperty instances returned by expressions such as A::x and A()::x",
// This must be done after contents of functions are extracted into separate classes, or else the `$$delegatedProperties` // This must be done after contents of functions are extracted into separate classes, or else the `$$delegatedProperties`
// field will end up in the wrong class (not the one that declares the delegated property). // field will end up in the wrong class (not the one that declares the delegated property).
prerequisite = setOf(functionReferencePhase) prerequisite = setOf(functionReferencePhase, suspendLambdaPhase)
) )
internal class PropertyReferenceLowering(val context: JvmBackendContext) : ClassLoweringPass { internal class PropertyReferenceLowering(val context: JvmBackendContext) : ClassLoweringPass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
LineBreakpoint created at coroutine.kt:17 LineBreakpoint created at coroutine.kt:17
Run Java Run Java
Connected to the target VM Connected to the target VM