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))
|
symbolTable.referenceClass(builtIns.getFunction(parameterCount))
|
||||||
|
|
||||||
private val jvmFunctionClasses = storageManager.createMemoizedFunction { n: Int ->
|
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) {
|
for (i in 1..n) {
|
||||||
klass.addTypeParameter("P$i", irBuiltIns.anyNType, Variance.IN_VARIANCE)
|
klass.addTypeParameter("P$i", irBuiltIns.anyNType, Variance.IN_VARIANCE)
|
||||||
}
|
}
|
||||||
val returnType = klass.addTypeParameter("R", irBuiltIns.anyNType, Variance.OUT_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) {
|
for (i in 1..n) {
|
||||||
addValueParameter("p$i", klass.typeParameters[i - 1].defaultType)
|
addValueParameter("p$i", klass.typeParameters[i - 1].defaultType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun getJvmFunctionClass(parameterCount: Int): IrClassSymbol =
|
fun getJvmFunctionClass(parameterCount: Int): IrClassSymbol =
|
||||||
jvmFunctionClasses(parameterCount)
|
jvmFunctionClasses(parameterCount)
|
||||||
|
|
||||||
private val jvmSuspendFunctionClasses = storageManager.createMemoizedFunction { n: Int ->
|
private val jvmSuspendFunctionClasses = storageManager.createMemoizedFunction { n: Int ->
|
||||||
createClass(FqName("kotlin.jvm.functions.Function${n + 1}"), ClassKind.INTERFACE) { klass ->
|
createFunctionClass(n, true)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getJvmSuspendFunctionClass(parameterCount: Int): IrClassSymbol =
|
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
|
// 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,
|
// 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.
|
// 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)) {
|
if (lastLineNumber != lineNumber || irFunction.isSuspend || irFunction.isInvokeSuspendOfLambda(context)) {
|
||||||
lastLineNumber = lineNumber
|
lastLineNumber = lineNumber
|
||||||
mv.visitLineNumber(lineNumber, markNewLabel())
|
mv.visitLineNumber(lineNumber, markNewLabel())
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import org.jetbrains.org.objectweb.asm.Type
|
|||||||
|
|
||||||
class IrTypeMapper(private val context: JvmBackendContext) : KotlinTypeMapperBase() {
|
class IrTypeMapper(private val context: JvmBackendContext) : KotlinTypeMapperBase() {
|
||||||
internal val typeSystem = IrTypeCheckerContext(context.irBuiltIns)
|
internal val typeSystem = IrTypeCheckerContext(context.irBuiltIns)
|
||||||
|
|
||||||
private val IrTypeArgument.adjustedType
|
private val IrTypeArgument.adjustedType
|
||||||
get() = (this as? IrTypeProjection)?.type ?: context.irBuiltIns.anyNType
|
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 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 constructor = addPrimaryConstructorForLambda(info.arity, info.reference, parametersFields)
|
||||||
val secondaryConstructor = addSecondaryConstructorForLambda(constructor)
|
val secondaryConstructor = addSecondaryConstructorForLambda(constructor)
|
||||||
val invokeToOverride = functionNClass.functions.single {
|
val invokeToOverride = functionNClass.functions.single {
|
||||||
@@ -136,10 +139,11 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
|||||||
}
|
}
|
||||||
val invokeSuspend = addInvokeSuspendForLambda(info.function, parametersFields, receiverField)
|
val invokeSuspend = addInvokeSuspendForLambda(info.function, parametersFields, receiverField)
|
||||||
if (info.arity <= 1) {
|
if (info.arity <= 1) {
|
||||||
val create = addCreate(constructor, suspendLambda, info.arity, parametersFields, receiverField)
|
val singleParameterField = receiverField ?: parametersWithoutArguments.singleOrNull()
|
||||||
addInvoke(create, invokeSuspend, invokeToOverride, parametersFields, receiverField, isConstructorCall = false)
|
val create = addCreate(constructor, suspendLambda, info, parametersWithArguments, singleParameterField)
|
||||||
|
addInvoke(create, invokeSuspend, invokeToOverride, singleParameterField, emptyList(), isConstructorCall = false)
|
||||||
} else {
|
} else {
|
||||||
addInvoke(constructor, invokeSuspend, invokeToOverride, parametersFields, receiverField, isConstructorCall = true)
|
addInvoke(constructor, invokeSuspend, invokeToOverride, receiverField, parametersWithoutArguments, isConstructorCall = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
context.suspendLambdaToOriginalFunctionMap[this] = info.function
|
context.suspendLambdaToOriginalFunctionMap[this] = info.function
|
||||||
@@ -205,8 +209,8 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
|||||||
create: IrFunction,
|
create: IrFunction,
|
||||||
invokeSuspend: IrFunction,
|
invokeSuspend: IrFunction,
|
||||||
invokeToOverride: IrSimpleFunctionSymbol,
|
invokeToOverride: IrSimpleFunctionSymbol,
|
||||||
parametersFields: List<IrField>,
|
|
||||||
receiverField: IrField?,
|
receiverField: IrField?,
|
||||||
|
parametersWithoutArguments: List<IrField>,
|
||||||
isConstructorCall: Boolean
|
isConstructorCall: Boolean
|
||||||
) {
|
) {
|
||||||
val unitClass = context.irBuiltIns.unitClass
|
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()))
|
createCall.putValueArgument(if (receiverField != null) 1 else 0, irGet(function.valueParameters.last()))
|
||||||
}, "create")
|
}, "create")
|
||||||
// In old BE 'create' function was responsible for putting arguments into fields, but in IR_BE I do not generate create,
|
// 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 parameters (extension receiver is allowed, however)
|
// unless suspend lambda has no or one parameter, including extension receiver
|
||||||
// Thus, we put arguments into fields
|
// This is because 'create' is called only from 'createCoroutineUnintercepted' from stdlib. And we have only versions
|
||||||
if (function.valueParameters.size > create.valueParameters.size) {
|
// 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()) {
|
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 ->
|
+irReturn(irCall(invokeSuspend).also { invokeSuspendCall ->
|
||||||
@@ -241,27 +247,29 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
|||||||
private fun IrClass.addCreate(
|
private fun IrClass.addCreate(
|
||||||
constructor: IrFunction,
|
constructor: IrFunction,
|
||||||
superType: IrClass,
|
superType: IrClass,
|
||||||
arity: Int,
|
info: SuspendLambdaInfo,
|
||||||
parametersFields: List<IrField>,
|
parametersWithArguments: List<IrField>,
|
||||||
receiverField: IrField?
|
singleParameterField: IrField?
|
||||||
): IrFunction {
|
): IrFunction {
|
||||||
val create = superType.functions.single {
|
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() &&
|
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 ->
|
return addFunctionOverride(create).also { function ->
|
||||||
function.body = context.createIrBuilder(function.symbol).irBlockBody {
|
function.body = context.createIrBuilder(function.symbol).irBlockBody {
|
||||||
|
var index = 0
|
||||||
val constructorCall = irCall(constructor).also {
|
val constructorCall = irCall(constructor).also {
|
||||||
for ((i, field) in parametersFields.withIndex()) {
|
for ((i, field) in parametersWithArguments.withIndex()) {
|
||||||
it.putValueArgument(i, irGetField(irGet(function.dispatchReceiverParameter!!), field))
|
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)
|
assert(function.valueParameters.size == 2)
|
||||||
val result = irTemporary(constructorCall, "result")
|
val result = irTemporary(constructorCall, "result")
|
||||||
+irSetField(irGet(result), receiverField, irGet(function.valueParameters.first()))
|
+irSetField(irGet(result), singleParameterField, irGet(function.valueParameters.first()))
|
||||||
+irReturn(irGet(result))
|
+irReturn(irGet(result))
|
||||||
} else {
|
} else {
|
||||||
assert(function.valueParameters.size == 1)
|
assert(function.valueParameters.size == 1)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// WITH_COROUTINES
|
// 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")
|
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
|
||||||
|
|
||||||
@@ -93,7 +94,6 @@ fun box(): String {
|
|||||||
builder {
|
builder {
|
||||||
multipleLocalsInOneSlot()
|
multipleLocalsInOneSlot()
|
||||||
}
|
}
|
||||||
// TODO: Do not spill 0, since it is constant.
|
|
||||||
res = (continuation!! as BaseContinuationImpl).getSpilledVariableFieldMapping()!!.toMap()["I$1"] ?: "multipleLocalsInOneSlot fail 1"
|
res = (continuation!! as BaseContinuationImpl).getSpilledVariableFieldMapping()!!.toMap()["I$1"] ?: "multipleLocalsInOneSlot fail 1"
|
||||||
if (res != "first") {
|
if (res != "first") {
|
||||||
return "" + res
|
return "" + res
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
// KJS_WITH_FULL_RUNTIME
|
// KJS_WITH_FULL_RUNTIME
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// WITH_COROUTINES
|
// WITH_COROUTINES
|
||||||
// COMMON_COROUTINES_TEST
|
// COMMON_COROUTINES_TEST
|
||||||
|
|||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
// KJS_WITH_FULL_RUNTIME
|
// KJS_WITH_FULL_RUNTIME
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// WITH_COROUTINES
|
// WITH_COROUTINES
|
||||||
// COMMON_COROUTINES_TEST
|
// COMMON_COROUTINES_TEST
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// COMMON_COROUTINES_TEST
|
// COMMON_COROUTINES_TEST
|
||||||
// WITH_COROUTINES
|
// WITH_COROUTINES
|
||||||
|
|||||||
Reference in New Issue
Block a user