JVM_IR: move SuspendLambdaLowering next to FunctionReferenceLowering
#KT-42253 Fixed #KT-39434 Fixed
This commit is contained in:
+2
-2
@@ -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,
|
||||||
|
|||||||
+140
-212
@@ -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 {
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user