[IR] Align debugging of suspend lambdas with old BE

The existing backend restores LVs and parameters from the suspend lambda
fields used for spilling between suspension points, hence they are
visible in the debugger as local variables, plain and simple.

This PR introduces the same pattern to the IR backend, to bring the
debugging experience in line with the existing backend.

Both backends are still at the mercy of the liveness analysis
performed in the coroutine transformer where a liveness analysis
minimizes live ranges of entries in the LVT. E.g. an unused parameter
will be dropped entirely.

Adjusted existing test expectations accounting for the differences in
LV behavior.
This commit is contained in:
Kristoffer Andersen
2020-11-24 12:53:16 +01:00
committed by Alexander Udalov
parent 2be62c13b0
commit 8a5f260d04
12 changed files with 112 additions and 78 deletions
@@ -5,12 +5,10 @@
package org.jetbrains.kotlin.backend.jvm.lower package org.jetbrains.kotlin.backend.jvm.lower
import com.intellij.lang.jvm.source.JvmDeclarationSearch
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.common.ir.moveBodyTo
import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
@@ -29,12 +27,12 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedVariableDescriptor
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
@@ -43,6 +41,7 @@ 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.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
internal val suspendLambdaPhase = makeIrFilePhase( internal val suspendLambdaPhase = makeIrFilePhase(
::SuspendLambdaLowering, ::SuspendLambdaLowering,
@@ -191,7 +190,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
} }
val invokeSuspend = addInvokeSuspendForLambda(function, suspendLambda, parametersFields) val invokeSuspend = addInvokeSuspendForLambda(function, suspendLambda, parametersFields)
if (function.capturesCrossinline()) { if (function.capturesCrossinline()) {
addInvokeSuspendForInlineForLambda(invokeSuspend) addInvokeSuspendForInlineLambda(invokeSuspend)
} }
if (createToOverride != null) { if (createToOverride != null) {
addInvokeCallingCreate(addCreate(constructor, createToOverride, parametersFields), invokeSuspend, invokeToOverride) addInvokeCallingCreate(addCreate(constructor, createToOverride, parametersFields), invokeSuspend, invokeToOverride)
@@ -209,19 +208,37 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
it.valueParameters[0].type.isKotlinResult() it.valueParameters[0].type.isKotlinResult()
} }
return addFunctionOverride(superMethod, irFunction.startOffset, irFunction.endOffset).apply { return addFunctionOverride(superMethod, irFunction.startOffset, irFunction.endOffset).apply {
body = irFunction.moveBodyTo(this, mapOf())?.transform(object : IrElementTransformerVoid() { val localVals: List<IrVariable> = fields.mapIndexed { index, field ->
override fun visitGetValue(expression: IrGetValue): IrExpression { buildVariable(
val parameter = (expression.symbol.owner as? IrValueParameter)?.takeIf { it.parent == irFunction } parent = this,
?: return expression startOffset = UNDEFINED_OFFSET,
val field = fields[parameter.index + if (irFunction.extensionReceiverParameter != null) 1 else 0] endOffset = UNDEFINED_OFFSET,
origin = IrDeclarationOrigin.DEFINED,
name = field.name,
type = field.type
).apply {
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) val initializerBlock = IrBlockImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.type)
initializerBlock.statements += IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.symbol, field.type, receiver)
initializer = initializerBlock
} }
}, null) }
body = irFunction.moveBodyTo(this, mapOf())?.let { body ->
body.transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
val parameter = (expression.symbol.owner as? IrValueParameter)?.takeIf { it.parent == irFunction }
?: return expression
val lvar = localVals[parameter.index + if (irFunction.extensionReceiverParameter != null) 1 else 0]
return IrGetValueImpl(expression.startOffset, expression.endOffset, lvar.symbol)
}
}, null)
context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, localVals + body.statements)
}
} }
} }
private fun IrClass.addInvokeSuspendForInlineForLambda(invokeSuspend: IrSimpleFunction): IrSimpleFunction { private fun IrClass.addInvokeSuspendForInlineLambda(invokeSuspend: IrSimpleFunction): IrSimpleFunction {
return addFunction( return addFunction(
INVOKE_SUSPEND_METHOD_NAME + FOR_INLINE_SUFFIX, INVOKE_SUSPEND_METHOD_NAME + FOR_INLINE_SUFFIX,
context.irBuiltIns.anyNType, context.irBuiltIns.anyNType,
@@ -11,21 +11,10 @@ suspend fun foo(data: Data, body: suspend (Data) -> Unit) {
body(data) body(data)
} }
// Parameters (including anonymous destructuring parameters) are moved to fields in the Continuation class for the suspend lambda class.
// However, in non-IR, the fields are first stored in local variables, and they are not read directly (even for destructuring components).
// In IR, the fields are directly read from.
// METHOD : DataClassKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // METHOD : DataClassKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
// JVM_TEMPLATES
// VARIABLE : NAME=$dstr$x_param$y_param TYPE=LData; INDEX=2 // VARIABLE : NAME=$dstr$x_param$y_param TYPE=LData; INDEX=2
// VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=3 // VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=3
// VARIABLE : NAME=y_param TYPE=I INDEX=4 // VARIABLE : NAME=y_param TYPE=I INDEX=4
// VARIABLE : NAME=this TYPE=LDataClassKt$test$2; INDEX=0 // VARIABLE : NAME=this TYPE=LDataClassKt$test$2; INDEX=0
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1 // VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1
// JVM_IR_TEMPLATES
// VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=2
// VARIABLE : NAME=y_param TYPE=I INDEX=3
// VARIABLE : NAME=this TYPE=LDataClassKt$test$2; INDEX=0
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1
@@ -22,10 +22,6 @@ suspend fun test() = B.bar()
// Local function bodies (i.e., `A<R>.component3()`) are in a separate class (implementing FunctionN) for non-IR, and are static methods // Local function bodies (i.e., `A<R>.component3()`) are in a separate class (implementing FunctionN) for non-IR, and are static methods
// in the enclosing class for IR. Therefore the ordinal in the suspend lambda class name is different for non-IR (`$3`) vs IR (e.g., `$2`). // in the enclosing class for IR. Therefore the ordinal in the suspend lambda class name is different for non-IR (`$3`) vs IR (e.g., `$2`).
// //
// Parameters (including anonymous destructuring parameters) are moved to fields in the Continuation class for the suspend lambda class.
// However, in non-IR, the fields are first stored in local variables, and they are not read directly (even for destructuring components).
// In IR, the fields are directly read from.
// JVM_TEMPLATES // JVM_TEMPLATES
// METHOD : ExtensionComponentsKt$bar$3.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // METHOD : ExtensionComponentsKt$bar$3.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
// VARIABLE : NAME=$dstr$x_param$y_param$z_param TYPE=LA; INDEX=2 // VARIABLE : NAME=$dstr$x_param$y_param$z_param TYPE=LA; INDEX=2
@@ -37,6 +33,7 @@ suspend fun test() = B.bar()
// JVM_IR_TEMPLATES // JVM_IR_TEMPLATES
// METHOD : ExtensionComponentsKt$bar$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // METHOD : ExtensionComponentsKt$bar$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
// VARIABLE : NAME=$dstr$x_param$y_param$z_param TYPE=LA; INDEX=2
// VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=2 // VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=2
// VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=3 // VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=3
// VARIABLE : NAME=z_param TYPE=I INDEX=4 // VARIABLE : NAME=z_param TYPE=I INDEX=4
@@ -8,21 +8,10 @@ suspend fun test() = foo(A("OK", 1)) { (x_param, y_param) ->
x_param + (y_param.toString()) x_param + (y_param.toString())
} }
// Parameters (including anonymous destructuring parameters) are moved to fields in the Continuation class for the suspend lambda class.
// However, in non-IR, the fields are first stored in local variables, and they are not read directly (even for destructuring components).
// In IR, the fields are directly read from.
// METHOD : GenericKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // METHOD : GenericKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
// JVM_TEMPLATES
// VARIABLE : NAME=$dstr$x_param$y_param TYPE=LA; INDEX=2 // VARIABLE : NAME=$dstr$x_param$y_param TYPE=LA; INDEX=2
// VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=3 // VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=3
// VARIABLE : NAME=y_param TYPE=I INDEX=4 // VARIABLE : NAME=y_param TYPE=I INDEX=4
// VARIABLE : NAME=this TYPE=LGenericKt$test$2; INDEX=0 // VARIABLE : NAME=this TYPE=LGenericKt$test$2; INDEX=0
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1 // VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1
// JVM_IR_TEMPLATES
// VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=2
// VARIABLE : NAME=y_param TYPE=I INDEX=3
// VARIABLE : NAME=this TYPE=LGenericKt$test$2; INDEX=0
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1
@@ -7,13 +7,8 @@ suspend fun test() = foo(A("O", "K")) { i_param, (x_param, y_param), v_param ->
i_param.toString() + x_param + y_param + v_param i_param.toString() + x_param + y_param + v_param
} }
// Parameters (including anonymous destructuring parameters) are moved to fields in the Continuation class for the suspend lambda class.
// However, in non-IR, the fields are first stored in local variables, and they are not read directly (even for destructuring components).
// In IR, the fields are directly read from.
// METHOD : OtherParametersKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // METHOD : OtherParametersKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
// JVM_TEMPLATES
// VARIABLE : NAME=i_param TYPE=I INDEX=2 // VARIABLE : NAME=i_param TYPE=I INDEX=2
// VARIABLE : NAME=$dstr$x_param$y_param TYPE=LA; INDEX=3 // VARIABLE : NAME=$dstr$x_param$y_param TYPE=LA; INDEX=3
// VARIABLE : NAME=v_param TYPE=Ljava/lang/String; INDEX=4 // VARIABLE : NAME=v_param TYPE=Ljava/lang/String; INDEX=4
@@ -21,9 +16,3 @@ suspend fun test() = foo(A("O", "K")) { i_param, (x_param, y_param), v_param ->
// VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=6 // VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=6
// VARIABLE : NAME=this TYPE=LOtherParametersKt$test$2; INDEX=0 // VARIABLE : NAME=this TYPE=LOtherParametersKt$test$2; INDEX=0
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1 // VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1
// JVM_IR_TEMPLATES
// VARIABLE : NAME=x_param TYPE=Ljava/lang/String; INDEX=2
// VARIABLE : NAME=y_param TYPE=Ljava/lang/String; INDEX=3
// VARIABLE : NAME=this TYPE=LOtherParametersKt$test$2; INDEX=0
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1
@@ -11,9 +11,7 @@ suspend fun foo(data: Data, body: suspend Long.(String, Data, Int) -> Unit) {
1L.body("OK", data, 1) 1L.body("OK", data, 1)
} }
// Parameters (including anonymous destructuring parameters) are moved to fields in the Continuation class for the suspend lambda class. // The JVM and IR backend differ in naming scheme of captured receiver paramters in suspend lambdas
// However, in non-IR, the fields are first stored in local variables, and they are not read directly (even for destructuring components).
// In IR, the fields are directly read from.
// METHOD : ParametersKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; // METHOD : ParametersKt$test$2.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
@@ -28,7 +26,11 @@ suspend fun foo(data: Data, body: suspend Long.(String, Data, Int) -> Unit) {
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1 // VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1
// JVM_IR_TEMPLATES // JVM_IR_TEMPLATES
// VARIABLE : NAME=x TYPE=Ljava/lang/String; INDEX=2 // VARIABLE : NAME=$dstr$x$_u24__u24$z TYPE=LData; INDEX=*
// VARIABLE : NAME=z TYPE=I INDEX=3 // VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=*
// VARIABLE : NAME=this TYPE=LParametersKt$test$2; INDEX=0 // VARIABLE : NAME=i TYPE=I INDEX=*
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=1 // VARIABLE : NAME=p$ TYPE=J INDEX=*
// VARIABLE : NAME=str TYPE=Ljava/lang/String; INDEX=*
// VARIABLE : NAME=this TYPE=LParametersKt$test$2; INDEX=*
// VARIABLE : NAME=x TYPE=Ljava/lang/String; INDEX=*
// VARIABLE : NAME=z TYPE=I INDEX=*
@@ -14,14 +14,12 @@ fun main(args: Array<String>) {
@BuilderInference @BuilderInference
suspend fun SequenceScope<Int>.awaitSeq(): Int = 42 suspend fun SequenceScope<Int>.awaitSeq(): Int = 42
// label numbers differ in BEs
// JVM_TEMPLATES
// 1 LOCALVARIABLE a I L[0-9]+ L18 // 1 LOCALVARIABLE a I L[0-9]+ L18
// 1 LINENUMBER 9 L19
/* TODO: JVM_IR does not generate LINENUMBER at the end of the lambda */ /* TODO: JVM_IR does not generate LINENUMBER at the end of the lambda */
// JVM_IR_TEMPLATES // JVM_TEMPLATES
// 1 LOCALVARIABLE a I L[0-9]+ L16 // 1 LINENUMBER 9 L19
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
@@ -13,13 +13,8 @@ suspend fun box() = foo(A()) { (x_param, _, y_param) ->
x_param + y_param x_param + y_param
} }
// Parameters (including anonymous destructuring parameters) are moved to fields in the Continuation class for the suspend lambda class. // TODO: The backends disagree on the local variables in invoke/invokeSuspend methods
// However, in non-IR, the fields are first stored in local variables, and they are not read directly (even for destructuring components).
// In IR, the fields are directly read from.
// The local variable for destructuring suspend lambda arguments, in this case
// `$dstr$x_param$_u24__u24$y_param`, is moved to a field in the IR backend,
// so does not figure in the LVT.
// LOCAL VARIABLES // LOCAL VARIABLES
// test.kt:12 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation // test.kt:12 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
@@ -41,16 +36,14 @@ suspend fun box() = foo(A()) { (x_param, _, y_param) ->
// LOCAL VARIABLES // LOCAL VARIABLES
// test.kt:12 invokeSuspend: // test.kt:12 invokeSuspend:
// test.kt:5 component1: // test.kt:5 component1:
// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A
// test.kt:7 component3:
// LOCAL VARIABLES JVM // LOCAL VARIABLES JVM
// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A // test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A
// test.kt:7 component3:
// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A
// LOCAL VARIABLES JVM_IR // LOCAL VARIABLES JVM_IR
// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit // test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A, x_param:java.lang.String="O":java.lang.String
// test.kt:7 component3:
// test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, x_param:java.lang.String="O":java.lang.String
// LOCAL VARIABLES // LOCAL VARIABLES
// test.kt:13 invokeSuspend: $result:java.lang.Object=kotlin.Unit, x_param:java.lang.String="O":java.lang.String, y_param:java.lang.String="K":java.lang.String // test.kt:13 invokeSuspend: $result:java.lang.Object=kotlin.Unit, x_param:java.lang.String="O":java.lang.String, y_param:java.lang.String="K":java.lang.String
@@ -606,6 +606,11 @@ public class IrKotlinEvaluateExpressionTestGenerated extends AbstractIrKotlinEva
runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/anyUpdateVariable.kt"); runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/anyUpdateVariable.kt");
} }
@TestMetadata("capturedReceiverName.kt")
public void testCapturedReceiverName() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/capturedReceiverName.kt");
}
@TestMetadata("primitivesCoertion.kt") @TestMetadata("primitivesCoertion.kt")
public void testPrimitivesCoertion() throws Exception { public void testPrimitivesCoertion() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/primitivesCoertion.kt"); runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/primitivesCoertion.kt");
@@ -605,6 +605,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/anyUpdateVariable.kt"); runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/anyUpdateVariable.kt");
} }
@TestMetadata("capturedReceiverName.kt")
public void testCapturedReceiverName() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/capturedReceiverName.kt");
}
@TestMetadata("primitivesCoertion.kt") @TestMetadata("primitivesCoertion.kt")
public void testPrimitivesCoertion() throws Exception { public void testPrimitivesCoertion() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/primitivesCoertion.kt"); runTest("idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/coroutines/primitivesCoertion.kt");
@@ -0,0 +1,40 @@
package capturedReceiverName
import kotlin.sequences.*
import kotlin.coroutines.*
fun builder(c: suspend () -> Unit) {
c.startCoroutine(object : Continuation<Unit>{
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
result.getOrThrow()
}
})
}
fun main(args: Array<String>) {
builder {
var s = "OK"
s = strChanger(s) { character ->
//Breakpoint!
character != 'a' // (2)
}
println(s)
}
}
suspend fun strChanger(str: String, pred: suspend (Char) -> Boolean): String {
var result = ""
str.forEach {
if (pred(it)) {
result += it
}
}
return result
}
// EXPRESSION: character
// RESULT: 79: C
// 79.toChar() == 'O'
@@ -0,0 +1,10 @@
LineBreakpoint created at capturedReceiverName.kt:22
Run Java
Connected to the target VM
capturedReceiverName.kt:22
Compile bytecode for character
capturedReceiverName.kt:22
Disconnected from the target VM
Process finished with exit code 0
OK