JVM_IR: Generate 'create' for suspend lambdas with one parameter
This commit is contained in:
@@ -209,36 +209,28 @@ class JvmSymbols(
|
||||
symbolTable.referenceClass(builtIns.getFunction(parameterCount))
|
||||
|
||||
private val jvmFunctionClasses = storageManager.createMemoizedFunction { n: Int ->
|
||||
createClass(FqName("kotlin.jvm.functions.Function$n"), ClassKind.INTERFACE) { klass ->
|
||||
createFunctionClass(n, false)
|
||||
}
|
||||
|
||||
private fun createFunctionClass(n: Int, isSuspend: Boolean): IrClassSymbol =
|
||||
createClass(FqName("kotlin.jvm.functions.Function${n + if (isSuspend) 1 else 0}"), ClassKind.INTERFACE) { klass ->
|
||||
for (i in 1..n) {
|
||||
klass.addTypeParameter("P$i", irBuiltIns.anyNType, Variance.IN_VARIANCE)
|
||||
}
|
||||
val returnType = klass.addTypeParameter("R", irBuiltIns.anyNType, Variance.OUT_VARIANCE)
|
||||
|
||||
klass.addFunction("invoke", returnType.defaultType, Modality.ABSTRACT).apply {
|
||||
klass.addFunction("invoke", returnType.defaultType, Modality.ABSTRACT, isSuspend = isSuspend).apply {
|
||||
for (i in 1..n) {
|
||||
addValueParameter("p$i", klass.typeParameters[i - 1].defaultType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getJvmFunctionClass(parameterCount: Int): IrClassSymbol =
|
||||
jvmFunctionClasses(parameterCount)
|
||||
|
||||
private val jvmSuspendFunctionClasses = storageManager.createMemoizedFunction { n: Int ->
|
||||
createClass(FqName("kotlin.jvm.functions.Function${n + 1}"), ClassKind.INTERFACE) { klass ->
|
||||
for (i in 1..n) {
|
||||
klass.addTypeParameter("P$i", irBuiltIns.anyNType, Variance.IN_VARIANCE)
|
||||
}
|
||||
val returnType = klass.addTypeParameter("R", irBuiltIns.anyNType, Variance.OUT_VARIANCE)
|
||||
|
||||
klass.addFunction("invoke", returnType.defaultType, Modality.ABSTRACT, isSuspend = true).apply {
|
||||
for (i in 1..n) {
|
||||
addValueParameter("p$i", klass.typeParameters[i - 1].defaultType)
|
||||
}
|
||||
}
|
||||
}
|
||||
createFunctionClass(n, true)
|
||||
}
|
||||
|
||||
fun getJvmSuspendFunctionClass(parameterCount: Int): IrClassSymbol =
|
||||
|
||||
+1
@@ -152,6 +152,7 @@ class ExpressionCodegen(
|
||||
// State-machine builder splits the sequence of instructions into states inside state-machine, adding additional LINENUMBERs
|
||||
// between them for debugger to stop on suspension. Thus, it requires as much LINENUMBER information as possible to be present,
|
||||
// otherwise, any exception will have incorrect line number. See elvisLineNumber.kt test.
|
||||
// TODO: Remove unneeded LINENUMBERs after building the state-machine.
|
||||
if (lastLineNumber != lineNumber || irFunction.isSuspend || irFunction.isInvokeSuspendOfLambda(context)) {
|
||||
lastLineNumber = lineNumber
|
||||
mv.visitLineNumber(lineNumber, markNewLabel())
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class IrTypeMapper(private val context: JvmBackendContext) : KotlinTypeMapperBase() {
|
||||
internal val typeSystem = IrTypeCheckerContext(context.irBuiltIns)
|
||||
|
||||
private val IrTypeArgument.adjustedType
|
||||
get() = (this as? IrTypeProjection)?.type ?: context.irBuiltIns.anyNType
|
||||
|
||||
|
||||
+27
-19
@@ -129,6 +129,9 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
}
|
||||
|
||||
val parametersFields = info.function.valueParameters.map { addField(it.name.asString(), it.type) }
|
||||
val parametersWithoutArguments = parametersFields.withIndex()
|
||||
.mapNotNull { (i, field) -> if (info.reference.getValueArgument(i) == null) field else null }
|
||||
val parametersWithArguments = parametersFields - parametersWithoutArguments
|
||||
val constructor = addPrimaryConstructorForLambda(info.arity, info.reference, parametersFields)
|
||||
val secondaryConstructor = addSecondaryConstructorForLambda(constructor)
|
||||
val invokeToOverride = functionNClass.functions.single {
|
||||
@@ -136,10 +139,11 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
}
|
||||
val invokeSuspend = addInvokeSuspendForLambda(info.function, parametersFields, receiverField)
|
||||
if (info.arity <= 1) {
|
||||
val create = addCreate(constructor, suspendLambda, info.arity, parametersFields, receiverField)
|
||||
addInvoke(create, invokeSuspend, invokeToOverride, parametersFields, receiverField, isConstructorCall = false)
|
||||
val singleParameterField = receiverField ?: parametersWithoutArguments.singleOrNull()
|
||||
val create = addCreate(constructor, suspendLambda, info, parametersWithArguments, singleParameterField)
|
||||
addInvoke(create, invokeSuspend, invokeToOverride, singleParameterField, emptyList(), isConstructorCall = false)
|
||||
} else {
|
||||
addInvoke(constructor, invokeSuspend, invokeToOverride, parametersFields, receiverField, isConstructorCall = true)
|
||||
addInvoke(constructor, invokeSuspend, invokeToOverride, receiverField, parametersWithoutArguments, isConstructorCall = true)
|
||||
}
|
||||
|
||||
context.suspendLambdaToOriginalFunctionMap[this] = info.function
|
||||
@@ -205,8 +209,8 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
create: IrFunction,
|
||||
invokeSuspend: IrFunction,
|
||||
invokeToOverride: IrSimpleFunctionSymbol,
|
||||
parametersFields: List<IrField>,
|
||||
receiverField: IrField?,
|
||||
parametersWithoutArguments: List<IrField>,
|
||||
isConstructorCall: Boolean
|
||||
) {
|
||||
val unitClass = context.irBuiltIns.unitClass
|
||||
@@ -222,12 +226,14 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
}
|
||||
createCall.putValueArgument(if (receiverField != null) 1 else 0, irGet(function.valueParameters.last()))
|
||||
}, "create")
|
||||
// In old BE 'create' function was responsible for putting arguments into fields, but in IR_BE I do not generate create,
|
||||
// unless suspend lambda has no parameters (extension receiver is allowed, however)
|
||||
// Thus, we put arguments into fields
|
||||
if (function.valueParameters.size > create.valueParameters.size) {
|
||||
// In old BE 'create' function was responsible for putting arguments into fields, but in IR_BE we do not generate create,
|
||||
// unless suspend lambda has no or one parameter, including extension receiver
|
||||
// This is because 'create' is called only from 'createCoroutineUnintercepted' from stdlib. And we have only versions
|
||||
// for suspend lambdas without parameters and for ones with exactly one parameter (or extension receiver).
|
||||
// Thus, we put arguments into fields in 'invoke'.
|
||||
if (parametersWithoutArguments.isNotEmpty()) {
|
||||
for ((index, param) in function.valueParameters.drop(if (receiverField != null) 1 else 0).dropLast(1).withIndex()) {
|
||||
+irSetField(irGet(newlyCreatedObject), parametersFields[index], irGet(param))
|
||||
+irSetField(irGet(newlyCreatedObject), parametersWithoutArguments[index], irGet(param))
|
||||
}
|
||||
}
|
||||
+irReturn(irCall(invokeSuspend).also { invokeSuspendCall ->
|
||||
@@ -241,27 +247,29 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
private fun IrClass.addCreate(
|
||||
constructor: IrFunction,
|
||||
superType: IrClass,
|
||||
arity: Int,
|
||||
parametersFields: List<IrField>,
|
||||
receiverField: IrField?
|
||||
info: SuspendLambdaInfo,
|
||||
parametersWithArguments: List<IrField>,
|
||||
singleParameterField: IrField?
|
||||
): IrFunction {
|
||||
val create = superType.functions.single {
|
||||
it.name.asString() == "create" && it.valueParameters.size == arity + 1 &&
|
||||
it.name.asString() == "create" && it.valueParameters.size == info.arity + 1 &&
|
||||
it.valueParameters.last().type.isContinuation() &&
|
||||
if (arity == 1) it.valueParameters.first().type.isNullableAny() else true
|
||||
if (info.arity == 1) it.valueParameters.first().type.isNullableAny() else true
|
||||
}
|
||||
return addFunctionOverride(create).also { function ->
|
||||
function.body = context.createIrBuilder(function.symbol).irBlockBody {
|
||||
var index = 0
|
||||
val constructorCall = irCall(constructor).also {
|
||||
for ((i, field) in parametersFields.withIndex()) {
|
||||
it.putValueArgument(i, irGetField(irGet(function.dispatchReceiverParameter!!), field))
|
||||
for ((i, field) in parametersWithArguments.withIndex()) {
|
||||
if (info.reference.getValueArgument(i) == null) continue
|
||||
it.putValueArgument(index++, irGetField(irGet(function.dispatchReceiverParameter!!), field))
|
||||
}
|
||||
it.putValueArgument(parametersFields.size, irGet(function.valueParameters.last()))
|
||||
it.putValueArgument(index, irGet(function.valueParameters.last()))
|
||||
}
|
||||
if (receiverField != null) {
|
||||
if (singleParameterField != null) {
|
||||
assert(function.valueParameters.size == 2)
|
||||
val result = irTemporary(constructorCall, "result")
|
||||
+irSetField(irGet(result), receiverField, irGet(function.valueParameters.first()))
|
||||
+irSetField(irGet(result), singleParameterField, irGet(function.valueParameters.first()))
|
||||
+irReturn(irGet(result))
|
||||
} else {
|
||||
assert(function.valueParameters.size == 1)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// TODO: Merge the test with debuggerMetadata when KT-28016 is fixed, particulary, do not spill 0, since it is constant.
|
||||
|
||||
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
|
||||
|
||||
@@ -93,7 +94,6 @@ fun box(): String {
|
||||
builder {
|
||||
multipleLocalsInOneSlot()
|
||||
}
|
||||
// TODO: Do not spill 0, since it is constant.
|
||||
res = (continuation!! as BaseContinuationImpl).getSpilledVariableFieldMapping()!!.toMap()["I$1"] ?: "multipleLocalsInOneSlot fail 1"
|
||||
if (res != "first") {
|
||||
return "" + res
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// KJS_WITH_FULL_RUNTIME
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// KJS_WITH_FULL_RUNTIME
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// COMMON_COROUTINES_TEST
|
||||
// WITH_COROUTINES
|
||||
|
||||
Reference in New Issue
Block a user